mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-21 23:44:42 +00:00
Compare commits
230
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
688450bf5a | ||
|
|
e994133177 | ||
|
|
68713ef9a7 | ||
|
|
dbacbc8874 | ||
|
|
8382299a05 | ||
|
|
93b09cf449 | ||
|
|
dac3311b81 | ||
|
|
5b1ca7a533 | ||
|
|
e940c29110 | ||
|
|
6bb5ed1d73 | ||
|
|
19cc4e8a37 | ||
|
|
fd84ff09e1 | ||
|
|
3dce175f94 | ||
|
|
0aaa3263cf | ||
|
|
c4dcbd8c44 | ||
|
|
09ced50590 | ||
|
|
b2ab1a30a5 | ||
|
|
9eff3916b3 | ||
|
|
98472d1952 | ||
|
|
1ceafb69a5 | ||
|
|
f3f4b6576c | ||
|
|
e699d7dafc | ||
|
|
5eb6d186c0 | ||
|
|
edc9409e56 | ||
|
|
d015719fae | ||
|
|
afc69e447e | ||
|
|
54fbbd734f | ||
|
|
d29872e69e | ||
|
|
99fb9972f9 | ||
|
|
f2e7d0fae2 | ||
|
|
7ff2e22f17 | ||
|
|
4eee66b0ef | ||
|
|
19fe7ba6bf | ||
|
|
7b522582fb | ||
|
|
7594e51e2b | ||
|
|
b3f0ff3662 | ||
|
|
8ce304a0c3 | ||
|
|
f893b2b5be | ||
|
|
db868c6df7 | ||
|
|
6225d8d1f0 | ||
|
|
ed975b459e | ||
|
|
652a6f5c4d | ||
|
|
a7a94099a8 | ||
|
|
71a3cbc334 | ||
|
|
800b2440b3 | ||
|
|
f50a49e7a3 | ||
|
|
92ece4dfb6 | ||
|
|
174225c60e | ||
|
|
3071410a22 | ||
|
|
5c5777ffc5 | ||
|
|
8dd8b93656 | ||
|
|
3b2d455b4a | ||
|
|
b0dfa22903 | ||
|
|
88b1c28d88 | ||
|
|
5b85d7859a | ||
|
|
468eed58d5 | ||
|
|
34c17be474 | ||
|
|
5dedd6b1c1 | ||
|
|
25e8e81686 | ||
|
|
1f7ab03bbb | ||
|
|
86f3cb7288 | ||
|
|
a1b8bf23b4 | ||
|
|
1f21d5ea4c | ||
|
|
f8ff7201d7 | ||
|
|
3567c036c3 | ||
|
|
0cf1f087ad | ||
|
|
209f60727e | ||
|
|
56624fc079 | ||
|
|
218c5243e3 | ||
|
|
560a979e0e | ||
|
|
1debe4c7c8 | ||
|
|
2e2aa77727 | ||
|
|
162ebf6545 | ||
|
|
cf09eff640 | ||
|
|
2d503a0edb | ||
|
|
7f7f0a099a | ||
|
|
109f5d1faa | ||
|
|
38a5d40b64 | ||
|
|
9b55f6fc56 | ||
|
|
83879f5cfe | ||
|
|
083ac5a7e0 | ||
|
|
a7eba377ba | ||
|
|
4ee1e6d9f1 | ||
|
|
24a63c10d0 | ||
|
|
0d805b2d43 | ||
|
|
7bc47fe6d7 | ||
|
|
ae50846257 | ||
|
|
ce466e7715 | ||
|
|
15885545b5 | ||
|
|
29fedcd390 | ||
|
|
fadd6b761f | ||
|
|
d857335901 | ||
|
|
93b4cbfb2c | ||
|
|
625334c6c8 | ||
|
|
943374856e | ||
|
|
e1d7fd92ad | ||
|
|
c10dc74818 | ||
|
|
92d9f2c18d | ||
|
|
d7488b6984 | ||
|
|
88ad3cd724 | ||
|
|
ba9e0b0c12 | ||
|
|
bbe7255901 | ||
|
|
51e55d3e3b | ||
|
|
f704dc0de9 | ||
|
|
e858280f30 | ||
|
|
f9b170204a | ||
|
|
0ed7415a0d | ||
|
|
7a0d50ecca | ||
|
|
19e035aa33 | ||
|
|
830970cb75 | ||
|
|
9b2987d0a2 | ||
|
|
76a4062f8b | ||
|
|
113717f955 | ||
|
|
c40ad58028 | ||
|
|
d6a7efcb7f | ||
|
|
910b89d6ce | ||
|
|
5554dfdd89 | ||
|
|
1657cd50fc | ||
|
|
57bf5e11b5 | ||
|
|
594ca47c85 | ||
|
|
6c5ee14c73 | ||
|
|
8e9d54b44b | ||
|
|
70805665bf | ||
|
|
ac243a706e | ||
|
|
aa382ce80d | ||
|
|
b7da508ccc | ||
|
|
47bd0ca647 | ||
|
|
c5d6c0ce98 | ||
|
|
6017d6b7a9 | ||
|
|
e31e06b288 | ||
|
|
84ac803a7d | ||
|
|
bcb970afb1 | ||
|
|
9d640b57ce | ||
|
|
4c83617847 | ||
|
|
5d7906972b | ||
|
|
666cd4a4f0 | ||
|
|
eb4d39e8b4 | ||
|
|
a1ad89a2e2 | ||
|
|
38108299fa | ||
|
|
189e2679f1 | ||
|
|
22ceb57052 | ||
|
|
0130c7cce6 | ||
|
|
18b6484731 | ||
|
|
f19ff59005 | ||
|
|
8786b5ee2a | ||
|
|
c7e23f9646 | ||
|
|
4043e3f71f | ||
|
|
08887f1147 | ||
|
|
13a0578ab7 | ||
|
|
888bf04ec2 | ||
|
|
d0822041ec | ||
|
|
e65a57bba9 | ||
|
|
2bb1b09789 | ||
|
|
b83dad4e24 | ||
|
|
4bb28f4286 | ||
|
|
721cdf20e7 | ||
|
|
cf248c5cbc | ||
|
|
151ca75e5f | ||
|
|
0e52b7aff4 | ||
|
|
3a0199a1e5 | ||
|
|
50e96095ba | ||
|
|
2f5bce6229 | ||
|
|
06f288ef76 | ||
|
|
ffcad9f1ec | ||
|
|
031dc8a31a | ||
|
|
62f50ce366 | ||
|
|
864c1c9c0d | ||
|
|
cfd433b328 | ||
|
|
ad97338f9b | ||
|
|
5d596f4fd0 | ||
|
|
aa14d79642 | ||
|
|
745d435a57 | ||
|
|
094a7c6a20 | ||
|
|
aedbefc38a | ||
|
|
0cd3cf9e1c | ||
|
|
422efcc89f | ||
|
|
ef24141713 | ||
|
|
820d4ff573 | ||
|
|
c1c0c2e82f | ||
|
|
f024f0ee5d | ||
|
|
a038b34e29 | ||
|
|
4cc516def6 | ||
|
|
bb1095eb78 | ||
|
|
4c11190be9 | ||
|
|
c6790fec70 | ||
|
|
8a7a5eeea1 | ||
|
|
34328c10bf | ||
|
|
c4d503c8d5 | ||
|
|
5fdf5141a5 | ||
|
|
9be00a2693 | ||
|
|
17778f32b9 | ||
|
|
bcbdad99bc | ||
|
|
1e6d2bb6fb | ||
|
|
e9ef122e7f | ||
|
|
57fee3e428 | ||
|
|
4ea45e937a | ||
|
|
dc80d79da0 | ||
|
|
57f68376fe | ||
|
|
f47cfa60c5 | ||
|
|
de78cd014d | ||
|
|
9db53f73e2 | ||
|
|
e5f0aca477 | ||
|
|
261f2da105 | ||
|
|
ef4738f920 | ||
|
|
f23348eda2 | ||
|
|
6edfaf3eb6 | ||
|
|
f48963a6d0 | ||
|
|
40611b4ebe | ||
|
|
6ce34efb52 | ||
|
|
0800c53aac | ||
|
|
c6ab05979c | ||
|
|
2c8d5d3d5d | ||
|
|
b76a311ddc | ||
|
|
315bf0d51f | ||
|
|
4cdbe50eb6 | ||
|
|
ead0f0fae1 | ||
|
|
cd10d38990 | ||
|
|
8cb21c97e3 | ||
|
|
0ad8ce77ef | ||
|
|
e358ffd666 | ||
|
|
496f9a9cd5 | ||
|
|
61025de0d6 | ||
|
|
f08049b960 | ||
|
|
746c8f54eb | ||
|
|
9fc1efa005 | ||
|
|
448eac8ff5 | ||
|
|
5a18d086e0 | ||
|
|
918ace2eb6 | ||
|
|
fe733967dc | ||
|
|
59f5c0cb12 |
@@ -43,6 +43,7 @@ body:
|
||||
- WinFsp (Local Drive)
|
||||
- FUSE-T
|
||||
- macFUSE
|
||||
- FUSE
|
||||
- WebDAV (Windows Explorer)
|
||||
- WebDAV (AppleScript)
|
||||
- WebDAV (gio)
|
||||
@@ -95,4 +96,4 @@ body:
|
||||
id: further-info
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
|
||||
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
|
||||
|
||||
+29
-2
@@ -8,16 +8,43 @@ updates:
|
||||
time: "06:00"
|
||||
timezone: "UTC"
|
||||
groups:
|
||||
all: # one PR for all dependencies
|
||||
java-test-dependencies:
|
||||
patterns:
|
||||
- "org.junit.jupiter:*"
|
||||
- "org.mockito:*"
|
||||
- "org.hamcrest:*"
|
||||
- "com.google.jimfs:jimfs"
|
||||
maven-build-plugins:
|
||||
patterns:
|
||||
- "org.apache.maven.plugins:*"
|
||||
- "org.jacoco:jacoco-maven-plugin"
|
||||
- "org.owasp:dependency-check-maven"
|
||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
||||
- "org.codehaus.mojo:license-maven-plugin"
|
||||
javafx:
|
||||
patterns:
|
||||
- "org.openjfx:*"
|
||||
java-production-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
exclude-patterns:
|
||||
- "org.openjfx:*"
|
||||
- "org.apache.maven.plugins:*"
|
||||
- "org.jacoco:jacoco-maven-plugin"
|
||||
- "org.owasp:dependency-check-maven"
|
||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
||||
- "org.codehaus.mojo:license-maven-plugin"
|
||||
- "org.junit.jupiter:*"
|
||||
- "org.mockito:*"
|
||||
- "org.hamcrest:*"
|
||||
- "com.google.jimfs:jimfs"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/" # even for `.github/workflows`
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
groups:
|
||||
all: # one PR for all actions
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
labels:
|
||||
|
||||
@@ -10,7 +10,8 @@ on:
|
||||
required: false
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 20
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: '21.0.1+12'
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
@@ -20,30 +21,54 @@ jobs:
|
||||
|
||||
build:
|
||||
name: Build AppImage
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [get-version]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
appimage-suffix: x86_64
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip'
|
||||
openjfx-sha: 'f522ac2ae4bdd61f0219b7b8d2058ff72a22f36a44378453bcfdcd82f8f5e08c'
|
||||
- os: [self-hosted, Linux, ARM64]
|
||||
appimage-suffix: aarch64
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: 'c0d80ebbe0aab404ef9ad8b46c05bf533a1e40b39b2720eebd9238d81f6326ca'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
java-package: 'jdk+fx'
|
||||
check-latest: true
|
||||
cache: 'maven'
|
||||
- name: Ensure major jfx version in pom equals in jdk
|
||||
shell: pwsh
|
||||
|
||||
- name: Download OpenJFX jmods
|
||||
id: download-jmods
|
||||
run: |
|
||||
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
|
||||
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
|
||||
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
|
||||
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
|
||||
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||
echo "${{ matrix.openjfx-sha }} openjfx-jmods.zip" | shasum -a256 --check
|
||||
mkdir -p openjfx-jmods
|
||||
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||
- name: Ensure major jfx version in pom and in jmods is the same
|
||||
run: |
|
||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||
|
||||
if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
|
||||
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION})"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pdependency-check,linux -DskipTests
|
||||
run: mvn -B clean package -Plinux -DskipTests
|
||||
- name: Patch target dir
|
||||
run: |
|
||||
cp LICENSE.txt target
|
||||
@@ -53,7 +78,7 @@ jobs:
|
||||
${JAVA_HOME}/bin/jlink
|
||||
--verbose
|
||||
--output runtime
|
||||
--module-path "${JAVA_HOME}/jmods"
|
||||
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net
|
||||
--strip-native-commands
|
||||
--no-header-files
|
||||
@@ -118,7 +143,7 @@ jobs:
|
||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||
- name: Download AppImageKit
|
||||
run: |
|
||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage -o appimagetool.AppImage
|
||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage
|
||||
chmod +x appimagetool.AppImage
|
||||
./appimagetool.AppImage --appimage-extract
|
||||
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
|
||||
@@ -130,8 +155,8 @@ jobs:
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Build AppImage
|
||||
run: >
|
||||
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-x86_64.AppImage
|
||||
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
|
||||
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage
|
||||
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync'
|
||||
--sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback"
|
||||
- name: Create detached GPG signatures
|
||||
run: |
|
||||
@@ -140,7 +165,7 @@ jobs:
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: appimage
|
||||
name: appimage-${{ matrix.appimage-suffix }}
|
||||
path: |
|
||||
cryptomator-*.AppImage
|
||||
cryptomator-*.AppImage.zsync
|
||||
|
||||
@@ -6,7 +6,8 @@ on:
|
||||
types: [labeled]
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 20
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: 21
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -17,10 +18,10 @@ jobs:
|
||||
name: Compile and Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Cache SonarCloud packages
|
||||
@@ -35,7 +36,7 @@ jobs:
|
||||
mvn -B verify
|
||||
jacoco:report
|
||||
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
||||
-Pcoverage,dependency-check
|
||||
-Pcoverage
|
||||
-Dsonar.projectKey=cryptomator_cryptomator
|
||||
-Dsonar.organization=cryptomator
|
||||
-Dsonar.host.url=https://sonarcloud.io
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Checks JDK version for minor updates
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 1 * *' # run once a month at the first day of month
|
||||
|
||||
env:
|
||||
JDK_VERSION: '21.0.1+12'
|
||||
JDK_VENDOR: zulu
|
||||
|
||||
jobs:
|
||||
jdk-current:
|
||||
name: Check out current version
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
|
||||
steps:
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: ${{ env.JDK_VERSION }}
|
||||
distribution: ${{ env.JDK_VENDOR }}
|
||||
check-latest: false
|
||||
- name: Read JAVA_VERSION_DATE and store in env variable
|
||||
id: get-data
|
||||
run: |
|
||||
date=$(cat ${JAVA_HOME}/release | grep "JAVA_VERSION_DATE=\"" | awk -F'=' '{print $2}' | tr -d '"')
|
||||
echo "jdk-date=${date}" >> "$GITHUB_OUTPUT"
|
||||
jdk-latest:
|
||||
name: Checkout latest jdk version
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
|
||||
jdk-version: ${{ steps.get-data.outputs.jdk-version}}
|
||||
steps:
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: ${{ env.JDK_VENDOR }}
|
||||
check-latest: true
|
||||
- name: Read JAVA_VERSION_DATE and store in env variable
|
||||
id: get-data
|
||||
run: |
|
||||
date=$(cat ${JAVA_HOME}/release | grep "JAVA_VERSION_DATE=\"" | awk -F'=' '{print $2}' | tr -d '"')
|
||||
echo "jdk-date=${date}" >> "$GITHUB_OUTPUT"
|
||||
version=$(cat ${JAVA_HOME}/release | grep "JAVA_RUNTIME_VERSION=\"" | awk -F'=' '{print $2}' | tr -d '"')
|
||||
echo "jdk-version=${version}" >> "$GITHUB_OUTPUT"
|
||||
notify:
|
||||
name: Notifies for jdk update
|
||||
runs-on: ubuntu-latest
|
||||
needs: [jdk-current, jdk-latest]
|
||||
if: ${{ needs.jdk-latest.outputs.jdk-date }} > ${{ needs.jdk-current.outputs.jdk-date }}
|
||||
steps:
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
SLACK_ICON: false
|
||||
SLACK_ICON_EMOJI: ':bot:'
|
||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||
SLACK_TITLE: "JDK update available"
|
||||
SLACK_MESSAGE: "Cryptomator-CI JDK can be upgraded to ${{ needs.jdk-latest.outputs.jdk-version }}. See https://github.com/cryptomator/cryptomator/wiki/How-to-update-the-build-JDK for instructions."
|
||||
SLACK_FOOTER: false
|
||||
MSG_MINIMAL: true
|
||||
@@ -16,16 +16,21 @@ on:
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 20
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: '21.0.1+12'
|
||||
COFFEELIBS_JDK: 21
|
||||
COFFEELIBS_JDK_VERSION: '21.0.1+12-0ppa1'
|
||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AMD64_HASH: 'f522ac2ae4bdd61f0219b7b8d2058ff72a22f36a44378453bcfdcd82f8f5e08c'
|
||||
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-aarch64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AARCH64_HASH: 'c0d80ebbe0aab404ef9ad8b46c05bf533a1e40b39b2720eebd9238d81f6326ca'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Debian Package
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- id: versions
|
||||
name: Get version information
|
||||
run: |
|
||||
@@ -39,22 +44,25 @@ jobs:
|
||||
run: |
|
||||
sudo add-apt-repository ppa:coffeelibs/openjdk
|
||||
sudo apt-get update
|
||||
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.JAVA_VERSION }} libgtk2.0-0
|
||||
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }} libgtk2.0-0
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
check-latest: true
|
||||
cache: 'maven'
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pdependency-check,linux -DskipTests
|
||||
run: mvn -B clean package -Plinux -DskipTests
|
||||
- name: Download OpenJFX jmods
|
||||
id: download-jmods
|
||||
run: |
|
||||
curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip
|
||||
echo "${{ env.OPENJFX_JMODS_AMD64_HASH }} openjfx-amd64.zip" | shasum -a256 --check
|
||||
mkdir -p jmods/amd64
|
||||
unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64
|
||||
curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip
|
||||
echo "${{ env.OPENJFX_JMODS_AARCH64_HASH }} openjfx-aarch64.zip" | shasum -a256 --check
|
||||
mkdir -p jmods/aarch64
|
||||
unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64
|
||||
- name: Ensure major jfx version in pom and in jmods is the same
|
||||
@@ -91,7 +99,8 @@ jobs:
|
||||
run: |
|
||||
cp -r dist/linux/debian/ pkgdir
|
||||
export RFC2822_TIMESTAMP=`date --rfc-2822`
|
||||
envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM}' < dist/linux/debian/rules > pkgdir/debian/rules
|
||||
export DISABLE_UPDATE_CHECK=${{ inputs.dput }}
|
||||
envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM} ${DISABLE_UPDATE_CHECK}' < dist/linux/debian/rules > pkgdir/debian/rules
|
||||
envsubst '${PPA_VERSION} ${RFC2822_TIMESTAMP}' < dist/linux/debian/changelog > pkgdir/debian/changelog
|
||||
find . -name "*.jar" >> pkgdir/debian/source/include-binaries
|
||||
mv pkgdir cryptomator_${{ inputs.ppaver }}
|
||||
@@ -141,4 +150,4 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
run: |
|
||||
artifacts=$(ls | grep cryptomator*.deb)
|
||||
gh release upload ${{ github.ref_name }} $artifacts
|
||||
gh release upload ${{ github.ref_name }} $artifacts
|
||||
|
||||
@@ -2,7 +2,7 @@ name: Update Error Database
|
||||
|
||||
on:
|
||||
discussion:
|
||||
types: [created, edited, category_changed, answered, unanswered]
|
||||
types: [created, edited, deleted, category_changed, answered, unanswered]
|
||||
discussion_comment:
|
||||
types: [created, edited, deleted]
|
||||
|
||||
@@ -12,6 +12,7 @@ jobs:
|
||||
if: github.event.discussion.category.name == 'Errors'
|
||||
steps:
|
||||
- name: Query Discussion Data
|
||||
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
|
||||
id: query-data
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
@@ -47,8 +48,13 @@ jobs:
|
||||
- name: Merge Error Code Data
|
||||
run: |
|
||||
jq -c '.' ${{ steps.get-gist.outputs.file }} > original.json
|
||||
echo $DISCUSSION | jq -c '.repository.discussion | .comments = .comments.totalCount | {(.id|tostring) : .}' > new.json
|
||||
jq -s '.[0] * .[1]' original.json new.json > merged.json
|
||||
if [ ! -z "$DISCUSSION" ]
|
||||
then
|
||||
echo $DISCUSSION | jq -c '.repository.discussion | .comments = .comments.totalCount | {(.id|tostring) : .}' > new.json
|
||||
jq -s '.[0] * .[1]' original.json new.json > merged.json
|
||||
else
|
||||
cat original.json | jq 'del(.[] | select(.url=="https://github.com/cryptomator/cryptomator/discussions/${{ github.event.discussion.number }}"))' > merged.json
|
||||
fi
|
||||
env:
|
||||
DISCUSSION: ${{ steps.query-data.outputs.result }}
|
||||
- name: Patch Gist
|
||||
|
||||
@@ -22,9 +22,8 @@ on:
|
||||
value: ${{ jobs.determine-version.outputs.type }}
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 20
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_CACHE: 'maven'
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: 21
|
||||
|
||||
jobs:
|
||||
determine-version:
|
||||
@@ -36,7 +35,7 @@ jobs:
|
||||
revNum: ${{ steps.versions.outputs.revNum }}
|
||||
type: ${{ steps.versions.outputs.type}}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Java
|
||||
@@ -44,7 +43,7 @@ jobs:
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: ${{ env.JAVA_CACHE }}
|
||||
cache: 'maven'
|
||||
- id: versions
|
||||
name: Get version information
|
||||
run: |
|
||||
|
||||
@@ -15,7 +15,8 @@ on:
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 20
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: '21.0.1+12'
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
@@ -36,24 +37,24 @@ jobs:
|
||||
output-suffix: x64
|
||||
xcode-path: '/Applications/Xcode_13.2.1.app'
|
||||
fuse-lib: macFUSE
|
||||
openjfx-url: https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-x64_bin-jmods.zip
|
||||
openjfx-sha: 55b8ff7453d59c89ae129f6c9c5ad7b09a5d359568811b376ac1766c14d6a17c
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-x64_bin-jmods.zip'
|
||||
openjfx-sha: '55b8ff7453d59c89ae129f6c9c5ad7b09a5d359568811b376ac1766c14d6a17c'
|
||||
- os: [self-hosted, macOS, ARM64]
|
||||
architecture: aarch64
|
||||
output-suffix: arm64
|
||||
xcode-path: '/Applications/Xcode_13.2.1.app'
|
||||
fuse-lib: FUSE-T
|
||||
openjfx-url: https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-aarch64_bin-jmods.zip
|
||||
openjfx-sha: c60f5f19aa847e0e620e0b011e5de68f2c6755641c2141cec27a0b89f612beaf
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: 'c60f5f19aa847e0e620e0b011e5de68f2c6755641c2141cec27a0b89f612beaf'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
java-package: 'jdk'
|
||||
architecture: ${{ matrix.architecture }}
|
||||
check-latest: true
|
||||
cache: 'maven'
|
||||
- name: Download OpenJFX jmods
|
||||
id: download-jmods
|
||||
@@ -61,7 +62,7 @@ jobs:
|
||||
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
|
||||
mkdir -p openjfx-jmods/
|
||||
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||
- name: Ensure major jfx version in pom and in jmods is the same
|
||||
run: |
|
||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
||||
@@ -71,14 +72,14 @@ jobs:
|
||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||
|
||||
if [ $POM_JFX_VERSION -ne $JMOD_VERSION ]; then
|
||||
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION_AMD64})"
|
||||
if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
|
||||
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
|
||||
exit 1
|
||||
fi
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pdependency-check,mac -DskipTests
|
||||
run: mvn -B clean package -Pmac -DskipTests
|
||||
- name: Patch target dir
|
||||
run: |
|
||||
cp LICENSE.txt target
|
||||
@@ -221,7 +222,6 @@ jobs:
|
||||
--app-drop-link 512 245
|
||||
--eula "dist/mac/dmg/resources/license.rtf"
|
||||
--icon ".background" 128 758
|
||||
--icon ".fseventsd" 320 758
|
||||
--icon ".VolumeIcon.icns" 512 758
|
||||
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
|
||||
env:
|
||||
|
||||
@@ -4,7 +4,8 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 20
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: 21
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -16,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Build and Test
|
||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage,dependency-check
|
||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage
|
||||
@@ -6,9 +6,6 @@ on:
|
||||
- 'release/**'
|
||||
- 'hotfix/**'
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 20
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -18,7 +15,7 @@ jobs:
|
||||
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- id: validate-pom-version
|
||||
name: Validate POM version
|
||||
run: |
|
||||
|
||||
@@ -14,11 +14,12 @@ on:
|
||||
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 20
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_CACHE: 'maven'
|
||||
JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_windows-x64_bin-jmods.zip'
|
||||
JFX_JMODS_HASH: '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: '21.0.1+12'
|
||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_windows-x64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AMD64_HASH: '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
|
||||
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
|
||||
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/download/1.0.0-beta9/winfsp-uninstaller.exe'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -38,20 +39,20 @@ jobs:
|
||||
LOOPBACK_ALIAS: 'cryptomator-vault'
|
||||
WIN_CONSOLE_FLAG: ''
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
java-package: 'jdk'
|
||||
cache: ${{ env.JAVA_CACHE }}
|
||||
check-latest: true
|
||||
cache: 'maven'
|
||||
- name: Download and extract JavaFX jmods from Gluon
|
||||
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
|
||||
run: |
|
||||
curl --output jfxjmods.zip -L "${{ env.JFX_JMODS_URL }}"
|
||||
if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.equals("${{ env.JFX_JMODS_HASH }}")) {
|
||||
throw "Wrong checksum of JMOD archive downloaded from ${{ env.JFX_JMODS_URL }}.";
|
||||
curl --output jfxjmods.zip -L "${{ env.OPENJFX_JMODS_AMD64 }}"
|
||||
if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.ToLower().equals("${{ env.OPENJFX_JMODS_AMD64_HASH }}")) {
|
||||
throw "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}.";
|
||||
}
|
||||
Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
|
||||
Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
|
||||
@@ -72,7 +73,7 @@ jobs:
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pdependency-check,win -DskipTests
|
||||
run: mvn -B clean package -Pwin -DskipTests
|
||||
- name: Patch target dir
|
||||
run: |
|
||||
cp LICENSE.txt target
|
||||
@@ -144,9 +145,29 @@ jobs:
|
||||
- name: Fix permissions
|
||||
run: attrib -r appdir/Cryptomator/Cryptomator.exe
|
||||
shell: pwsh
|
||||
- name: Extract integrations DLL for code signing
|
||||
- name: Extract jars with DLLs for Codesigning
|
||||
shell: pwsh
|
||||
run: gci ./appdir/Cryptomator/app/mods/ -File integrations-win-*.jar | ForEach-Object {Set-Location -Path $_.Directory; jar --file=$($_.FullName) --extract integrations.dll }
|
||||
run: |
|
||||
Add-Type -AssemblyName "System.io.compression.filesystem"
|
||||
$jarFolder = Resolve-Path ".\appdir\Cryptomator\app\mods"
|
||||
$jarExtractDir = New-Item -Path ".\appdir\jar-extract" -ItemType Directory
|
||||
|
||||
#for all jars inspect
|
||||
Get-ChildItem -Path $jarFolder -Filter "*.jar" | ForEach-Object {
|
||||
$jar = [Io.compression.zipfile]::OpenRead($_.FullName)
|
||||
if (@($jar.Entries | Where-Object {$_.Name.ToString().EndsWith(".dll")} | Select-Object -First 1).Count -gt 0) {
|
||||
#jars containing dlls extract
|
||||
Set-Location $jarExtractDir
|
||||
Expand-Archive -Path $_.FullName
|
||||
}
|
||||
$jar.Dispose()
|
||||
}
|
||||
- name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -Path appdir/jpackage-jmod -ItemType Directory
|
||||
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
|
||||
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
|
||||
- name: Codesign
|
||||
uses: skymatic/code-sign-action@v2
|
||||
with:
|
||||
@@ -155,12 +176,22 @@ jobs:
|
||||
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
|
||||
description: Cryptomator
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
folder: appdir/Cryptomator
|
||||
folder: appdir
|
||||
recursive: true
|
||||
- name: Repack signed DLL into jar
|
||||
- name: Replace DLLs inside jars with signed ones
|
||||
shell: pwsh
|
||||
run: |
|
||||
gci ./appdir/Cryptomator/app/mods/ -File integrations-win-*.jar | ForEach-Object {Set-Location -Path $_.Directory; jar --file=$($_.FullName) --update integrations.dll; Remove-Item integrations.dll}
|
||||
$jarExtractDir = Resolve-Path ".\appdir\jar-extract"
|
||||
$jarFolder = Resolve-Path ".\appdir\Cryptomator\app\mods"
|
||||
Get-ChildItem -Path $jarExtractDir | ForEach-Object {
|
||||
$jarName = $_.Name
|
||||
$jarFile = "${jarFolder}\${jarName}.jar"
|
||||
Set-Location $_
|
||||
Get-ChildItem -Path $_ -Recurse -File "*.dll" | ForEach-Object {
|
||||
# update jar with signed dll
|
||||
jar --file="$jarFile" --update $(Resolve-Path -Relative -Path $_)
|
||||
}
|
||||
}
|
||||
- name: Generate license for MSI
|
||||
run: >
|
||||
mvn -B license:add-third-party
|
||||
@@ -194,6 +225,7 @@ jobs:
|
||||
--file-associations dist/win/resources/FAvaultFile.properties
|
||||
env:
|
||||
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
|
||||
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
|
||||
- name: Codesign MSI
|
||||
uses: skymatic/code-sign-action@v2
|
||||
with:
|
||||
@@ -235,7 +267,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
needs: [get-version, build-msi]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download .msi
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -247,7 +279,8 @@ jobs:
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: ${{ env.JAVA_CACHE }}
|
||||
check-latest: true
|
||||
cache: 'maven'
|
||||
- name: Generate license for exe
|
||||
run: >
|
||||
mvn -B license:add-third-party
|
||||
@@ -261,8 +294,11 @@ jobs:
|
||||
shell: pwsh
|
||||
- name: Download WinFsp
|
||||
run: |
|
||||
$winfspUrl = (Select-String -Path ".\dist\win\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
|
||||
curl --output dist/win/bundle/resources/winfsp.msi -L $winfspUrl
|
||||
curl --output dist/win/bundle/resources/winfsp.msi -L ${{ env.WINFSP_MSI }}
|
||||
shell: pwsh
|
||||
- name: Download Legacy-WinFsp uninstaller
|
||||
run: |
|
||||
curl --output dist/win/bundle/resources/winfsp-uninstaller.exe -L ${{ env.WINFSP_UNINSTALLER }}
|
||||
shell: pwsh
|
||||
- name: Compile to wixObj file
|
||||
run: >
|
||||
|
||||
Generated
+11
-12
@@ -14,10 +14,10 @@
|
||||
<option name="dagger.fastInit" value="enabled" />
|
||||
<option name="dagger.formatGeneratedSource" value="enabled" />
|
||||
<processorPath useClasspath="false">
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.45/dagger-compiler-2.45.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.45/dagger-2.45.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.48.1/dagger-compiler-2.48.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.48.1/dagger-2.48.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.45/dagger-producers-2.45.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.48.1/dagger-producers-2.48.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
||||
@@ -26,20 +26,19 @@
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.7.1/error_prone_annotations-2.7.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.45/dagger-spi-2.45.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.7.0-1.0.6/symbol-processing-api-1.7.0-1.0.6.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.7.0/kotlin-stdlib-1.7.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.0/kotlin-stdlib-common-1.7.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.48.1/dagger-spi-2.48.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.9.0-1.0.12/symbol-processing-api-1.9.0-1.0.12.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.0/kotlin-stdlib-jdk8-1.7.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.0/kotlin-stdlib-jdk7-1.7.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.5.0/kotlinx-metadata-jvm-0.5.0.jar" />
|
||||
</processorPath>
|
||||
<module name="cryptomator" />
|
||||
</profile>
|
||||
|
||||
Generated
+1
-1
@@ -8,7 +8,7 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_20_PREVIEW" project-jdk-name="20" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21_PREVIEW" project-jdk-name="21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -30,9 +30,10 @@ Cryptomator is provided free of charge as an open-source project despite the hig
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="https://mowcapital.com/"><img src="https://cryptomator.org/img/sponsors/mowcapital.svg" alt="Mow Capital" height="40"></a></td>
|
||||
<td><a href="https://mowcapital.com/"><img src="https://cryptomator.org/img/sponsors/mowcapital.svg" alt="Mow Capital" height="28"></a></td>
|
||||
<td><a href="https://www.easeus.com/"><img src="https://cryptomator.org/img/sponsors/easeus.png" alt="EaseUS" height="40"></a></td>
|
||||
<td><a href="https://www.hassmann-it-forensik.de/"><img src="https://cryptomator.org/img/sponsors/hassmannitforensik.png" alt="Hassmann IT-Forensik" height="40"></a></td>
|
||||
<td><a href="https://ente.io/"><img src="https://cryptomator.org/img/sponsors/ente.svg" alt="Ente" height="58"></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Vendored
+6
-4
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)
|
||||
REVISION_NO=`git rev-list --count HEAD`
|
||||
@@ -10,6 +11,7 @@ command -v curl >/dev/null 2>&1 || { echo >&2 "curl not found."; exit 1; }
|
||||
|
||||
VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||
SEMVER_STR=${VERSION}
|
||||
MACHINE_TYPE=$(uname -m)
|
||||
|
||||
mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
|
||||
|
||||
@@ -83,17 +85,17 @@ ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.App
|
||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||
|
||||
# load AppImageTool
|
||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage -o /tmp/appimagetool.AppImage
|
||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${MACHINE_TYPE}.AppImage -o /tmp/appimagetool.AppImage
|
||||
chmod +x /tmp/appimagetool.AppImage
|
||||
|
||||
# create AppImage
|
||||
/tmp/appimagetool.AppImage \
|
||||
Cryptomator.AppDir \
|
||||
cryptomator-${SEMVER_STR}-x86_64.AppImage \
|
||||
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
|
||||
cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage \
|
||||
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${MACHINE_TYPE}.AppImage.zsync'
|
||||
|
||||
echo ""
|
||||
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-x86_64.AppImage"
|
||||
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage"
|
||||
echo ""
|
||||
echo >&2 "To clean up, run: rm -rf Cryptomator.AppDir appdir jni runtime squashfs-root; rm launcher-gtk2.properties /tmp/appimagetool.AppImage"
|
||||
echo ""
|
||||
@@ -66,6 +66,10 @@
|
||||
</content_rating>
|
||||
|
||||
<releases>
|
||||
<release date="2023-11-08" version="1.11.0"/>
|
||||
<release date="2023-09-20" version="1.10.1"/>
|
||||
<release date="2023-09-11" version="1.10.0"/>
|
||||
<release date="2023-08-11" version="1.9.4"/>
|
||||
<release date="2023-08-07" version="1.9.3"/>
|
||||
<release date="2023-07-24" version="1.9.2"/>
|
||||
<release date="2023-06-07" version="1.9.1"/>
|
||||
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@ Source: cryptomator
|
||||
Maintainer: Cryptobot <releases@cryptomator.org>
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Build-Depends: debhelper (>=10), coffeelibs-jdk-20, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
|
||||
Build-Depends: debhelper (>=10), coffeelibs-jdk-21 (= 21.0.1+12-0ppa1), libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
|
||||
Standards-Version: 4.5.0
|
||||
Homepage: https://cryptomator.org
|
||||
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
||||
|
||||
Vendored
+2
-1
@@ -4,7 +4,7 @@
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
JAVA_HOME = /usr/lib/jvm/java-20-coffeelibs
|
||||
JAVA_HOME = /usr/lib/jvm/java-21-coffeelibs
|
||||
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
||||
ifeq ($(DEB_BUILD_ARCH),amd64)
|
||||
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
||||
@@ -59,6 +59,7 @@ override_dh_auto_build:
|
||||
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"/usr/share/icons/hicolor/symbolic/apps\"" \
|
||||
--java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \
|
||||
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
|
||||
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
|
||||
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
|
||||
--resource-dir resources \
|
||||
--verbose
|
||||
|
||||
Vendored
+6
-6
@@ -49,21 +49,22 @@ fi
|
||||
# download and check jmods
|
||||
curl -L ${OPENJFX_JMODS} -o openjfx-jmods.zip
|
||||
mkdir -p openjfx-jmods/
|
||||
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods/
|
||||
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||
POM_JFX_VERSION=$(mvn -f../../../pom.xml help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||
|
||||
if [ $POM_JFX_VERSION -ne $JMOD_VERSION ]; then
|
||||
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
|
||||
exit 1
|
||||
if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
|
||||
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# compile
|
||||
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
|
||||
cp ../../../LICENSE.txt ../../../target
|
||||
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
|
||||
|
||||
# add runtime
|
||||
@@ -168,6 +169,5 @@ create-dmg \
|
||||
--app-drop-link 512 245 \
|
||||
--eula "resources/license.rtf" \
|
||||
--icon ".background" 128 758 \
|
||||
--icon ".fseventsd" 320 758 \
|
||||
--icon ".VolumeIcon.icns" 512 758 \
|
||||
${APP_NAME}-${VERSION_NO}.dmg dmg
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -4,4 +4,6 @@ installer
|
||||
*.wixobj
|
||||
*.pdb
|
||||
*.msi
|
||||
*.exe
|
||||
*.jmod
|
||||
license.rtf
|
||||
Vendored
+11
-4
@@ -63,10 +63,11 @@ if( !(Test-Path -Path $jfxJmodsZip) ) {
|
||||
$jmodsChecksumActual = $(Get-FileHash -Path $jfxJmodsZip -Algorithm SHA256).Hash
|
||||
if( $jmodsChecksumActual -ne $jfxJmodsChecksum ) {
|
||||
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $jfxJmodsChecksum, actual: $jmodsChecksumActual"
|
||||
exit 1;
|
||||
exit 1;
|
||||
}
|
||||
Expand-Archive -Force -Path $jfxJmodsZip -DestinationPath ".\resources\"
|
||||
Move-Item -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
|
||||
Expand-Archive -Path $jfxJmodsZip -Force -DestinationPath ".\resources\"
|
||||
Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods"
|
||||
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
|
||||
|
||||
|
||||
& "$Env:JAVA_HOME\bin\jlink" `
|
||||
@@ -143,6 +144,7 @@ try {
|
||||
|
||||
# create .msi
|
||||
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
|
||||
$Env:JP_WIXHELPER_DIR = "."
|
||||
& "$Env:JAVA_HOME\bin\jpackage" `
|
||||
--verbose `
|
||||
--type msi `
|
||||
@@ -174,10 +176,15 @@ $Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
|
||||
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
||||
|
||||
# download Winfsp
|
||||
$winfspMsiUrl= (Select-String -Path ".\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
|
||||
$winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
|
||||
Write-Output "Downloading ${winfspMsiUrl}..."
|
||||
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
|
||||
|
||||
# download legacy-winfsp uninstaller
|
||||
$winfspUninstaller= 'https://github.com/cryptomator/winfsp-uninstaller/releases/download/1.0.0-beta9/winfsp-uninstaller.exe'
|
||||
Write-Output "Downloading ${winfspUninstaller}..."
|
||||
Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninstaller.exe" # redirects are followed by default
|
||||
|
||||
# copy MSI to bundle resources
|
||||
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi"
|
||||
|
||||
|
||||
Vendored
+25
-20
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- For Built in variables, see https://wixtoolset.org/docs/tools/burn/builtin-variables/-->
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||
<!-- see https://wixtoolset.org/documentation/manual/v3/xsd/wix/bundle.html-->
|
||||
<!-- Attributes explicitly not used:
|
||||
@@ -10,21 +11,10 @@
|
||||
AboutUrl="$(var.AboutUrl)" HelpUrl="$(var.HelpUrl)" UpdateUrl="$(var.UpdateUrl)" Copyright="$(var.BundleCopyright)" IconSourceFile="bundle\resources\Cryptomator.ico">
|
||||
|
||||
<!-- detect outdated WinFsp installations -->
|
||||
<?include "resources\winFspMetaData.wxi" ?>
|
||||
<util:ProductSearch
|
||||
Variable="InstalledWinFspVersion"
|
||||
Variable="InstalledLegacyWinFspVersion"
|
||||
Result="version"
|
||||
UpgradeCode="82F812D9-4083-4EF1-8BC8-0F1EDA05B46B"
|
||||
/>
|
||||
<!-- Note: The bundle engine takes the Message format literaly -->
|
||||
<bal:Condition Message=
|
||||
"The WinFsp driver used by Cryptomator is outdated and must be removed before the installation.
|
||||
|
||||
1. Open the view of installed apps
|
||||
2. Search for "WinFsp"
|
||||
3. Uninstall the listed application
|
||||
4. Reboot your device
|
||||
5. Restart this installer">(InstalledWinFspVersion = v0.0.0.0) OR ($(var.BundledWinFspVersion) <= InstalledWinFspVersion)</bal:Condition>
|
||||
UpgradeCode="82F812D9-4083-4EF1-8BC8-0F1EDA05B46B"/>
|
||||
|
||||
<!-- for definition of the standard themes, see https://github.com/wixtoolset/wix3/blob/master/src/ext/BalExtension/wixstdba/Resources/-->
|
||||
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLargeLicense">
|
||||
@@ -36,26 +26,41 @@
|
||||
SuppressOptionsUI="yes"
|
||||
ThemeFile="bundle\customBootstrapperTheme.xml"
|
||||
LocalizationFile="bundle\customBootstrapperTheme.wxl"
|
||||
LogoFile="bundle\resources\logo.png"
|
||||
/>
|
||||
LogoFile="bundle\resources\logo.png"/>
|
||||
<Payload SourceFile="bundle\resources\logoSide.png" />
|
||||
</BootstrapperApplicationRef>
|
||||
|
||||
<Chain>
|
||||
<ExePackage Cache="yes" PerMachine="yes" Permanent="no"
|
||||
SourceFile="resources\winfsp-uninstaller.exe"
|
||||
DisplayName="Removing outdated WinFsp Driver"
|
||||
Description="Executable to remove old winfsp"
|
||||
DetectCondition="false"
|
||||
InstallCondition="(InstalledLegacyWinFspVersion <> v0.0.0.0) AND ((WixBundleAction = 7) OR (WixBundleAction = 5))">
|
||||
<CommandLine Condition="WixBundleUILevel <= 3" InstallArgument="-q -l "[WixBundleLog].winfsp-uninstaller.log"" RepairArgument="-q" UninstallArgument="-s" />
|
||||
<!-- XML allows line breaks in attributes, hence keep the line breaks here -->
|
||||
<CommandLine Condition="WixBundleUILevel > 3" InstallArgument="-l "[WixBundleLog].winfsp-uninstaller.log" -t "Cryptomator Installer" -m "Cryptomator requires a newer version of the WinFsp driver. The installer will now uninstall WinFsp, possibly reboot, and afterwards proceed with the installation.
|
||||
|
||||
Do you want to continue?"" RepairArgument="-q" UninstallArgument="-s" />
|
||||
<ExitCode Behavior="success" Value="0"/>
|
||||
<ExitCode Behavior="success" Value="1"/>
|
||||
<ExitCode Behavior="error" Value="2"/>
|
||||
<ExitCode Behavior="error" Value="3"/>
|
||||
<ExitCode Behavior="forceReboot" Value="4"/>
|
||||
<ExitCode Behavior="success" Value="5"/>
|
||||
</ExePackage>
|
||||
<!-- see https://wixtoolset.org/documentation/manual/v3/xsd/wix/msipackage.html-->
|
||||
<MsiPackage
|
||||
SourceFile="resources\Cryptomator.msi"
|
||||
CacheId="cryptomator-bundle-cryptomator"
|
||||
DisplayInternalUI="no"
|
||||
Visible="no"
|
||||
/>
|
||||
Visible="no"/>
|
||||
<MsiPackage
|
||||
SourceFile="resources\winfsp.msi"
|
||||
CacheId="cryptomator-bundle-winfsp"
|
||||
Visible="yes"
|
||||
DisplayInternalUI="no"
|
||||
Vital="no"
|
||||
Permanent="yes"
|
||||
/>
|
||||
Permanent="yes"/>
|
||||
</Chain>
|
||||
</Bundle>
|
||||
</Wix>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<!-- A version number MUST be prefixed with letter "v", otherwise it is considered a normal string -->
|
||||
<?define BundledWinFspVersion="v1.12.22339" ?>
|
||||
<?define BundledWinFspDownloadLink="https://github.com/winfsp/winfsp/releases/download/v1.12.22339/winfsp-1.12.22339.msi" ?> <!-- Only used by external build scripts -->
|
||||
</Include>
|
||||
Vendored
+1
-1
@@ -3,5 +3,5 @@
|
||||
::REPLACE ME
|
||||
|
||||
cd %~dp0
|
||||
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1^
|
||||
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\patchWebDAV.ps1^
|
||||
-LoopbackAlias %LOOPBACK_ALIAS%
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
:: see comments in file ./version170-migrate-settings.ps1
|
||||
|
||||
cd %~dp0
|
||||
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command .\version170-migrate-settings.ps1
|
||||
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\version170-migrate-settings.ps1
|
||||
|
||||
Vendored
+1
-1
@@ -70,7 +70,7 @@
|
||||
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
|
||||
<?endif?>
|
||||
|
||||
<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
|
||||
<Binary Id="JpCaDll" SourceFile="$(env.JP_WIXHELPER_DIR)\wixhelper.dll"/>
|
||||
<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />
|
||||
|
||||
<?ifndef SkipCryptomatorLegacyCheck ?>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptomator</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<version>1.11.1</version>
|
||||
<name>Cryptomator Desktop App</name>
|
||||
|
||||
<organization>
|
||||
@@ -26,45 +26,52 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.jdk.version>20</project.jdk.version>
|
||||
<project.jdk.version>21</project.jdk.version>
|
||||
|
||||
<!-- Group IDs of jars that need to stay on the class path for now -->
|
||||
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
|
||||
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
||||
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.6.7</cryptomator.cryptofs.version>
|
||||
<cryptomator.cryptofs.version>2.6.8</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.3.0</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.2.2</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.3.0-beta6</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>3.0.0</cryptomator.fuse.version>
|
||||
<cryptomator.integrations.win.version>1.2.4</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.2.2</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.4.0-beta2</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>4.0.0-beta5</cryptomator.fuse.version>
|
||||
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
|
||||
<cryptomator.webdav.version>2.0.3</cryptomator.webdav.version>
|
||||
<cryptomator.webdav.version>2.0.5</cryptomator.webdav.version>
|
||||
|
||||
<!-- 3rd party dependencies -->
|
||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||
<dagger.version>2.45</dagger.version>
|
||||
<commons-lang3.version>3.14.0</commons-lang3.version>
|
||||
<dagger.version>2.48.1</dagger.version>
|
||||
<easybind.version>2.2</easybind.version>
|
||||
<guava.version>32.0.1-jre</guava.version>
|
||||
<jackson.version>2.15.2</jackson.version>
|
||||
<guava.version>32.1.3-jre</guava.version>
|
||||
<jackson.version>2.16.0</jackson.version>
|
||||
<javafx.version>20.0.2</javafx.version>
|
||||
<jwt.version>4.4.0</jwt.version>
|
||||
<nimbus-jose.version>9.31</nimbus-jose.version>
|
||||
<logback.version>1.4.7</logback.version>
|
||||
<slf4j.version>2.0.7</slf4j.version>
|
||||
<tinyoauth2.version>0.5.1</tinyoauth2.version>
|
||||
<zxcvbn.version>1.7.0</zxcvbn.version>
|
||||
<nimbus-jose.version>9.37.1</nimbus-jose.version>
|
||||
<logback.version>1.4.12</logback.version>
|
||||
<slf4j.version>2.0.9</slf4j.version>
|
||||
<tinyoauth2.version>0.8.0</tinyoauth2.version>
|
||||
<zxcvbn.version>1.8.2</zxcvbn.version>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<junit.jupiter.version>5.9.3</junit.jupiter.version>
|
||||
<mockito.version>5.3.1</mockito.version>
|
||||
<junit.jupiter.version>5.10.1</junit.jupiter.version>
|
||||
<mockito.version>5.7.0</mockito.version>
|
||||
<hamcrest.version>2.2</hamcrest.version>
|
||||
|
||||
<!-- build-time dependencies -->
|
||||
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
|
||||
<dependency-check.version>8.1.2</dependency-check.version>
|
||||
<jacoco.version>0.8.9</jacoco.version>
|
||||
<jetbrains.annotations.version>24.1.0</jetbrains.annotations.version>
|
||||
<dependency-check.version>9.0.1</dependency-check.version>
|
||||
<jacoco.version>0.8.11</jacoco.version>
|
||||
<license-generator.version>2.3.0</license-generator.version>
|
||||
<junit-tree-reporter.version>1.2.1</junit-tree-reporter.version>
|
||||
<mvn-compiler.version>3.11.0</mvn-compiler.version>
|
||||
<mvn-resources.version>3.3.1</mvn-resources.version>
|
||||
<mvn-dependency.version>3.6.1</mvn-dependency.version>
|
||||
<mvn-surefire.version>3.2.2</mvn-surefire.version>
|
||||
<mvn-jar.version>3.3.0</mvn-jar.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -240,7 +247,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.jimfs</groupId>
|
||||
<artifactId>jimfs</artifactId>
|
||||
<version>1.2</version>
|
||||
<version>1.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -258,32 +265,32 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.10.1</version>
|
||||
<version>${mvn-compiler.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>${mvn-resources.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>${mvn-dependency.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M7</version>
|
||||
<version>${mvn-surefire.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<version>${license-generator.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>${mvn-jar.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
@@ -332,8 +339,22 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>me.fabriciorby</groupId>
|
||||
<artifactId>maven-surefire-junit5-tree-reporter</artifactId>
|
||||
<version>${junit-tree-reporter.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<argLine>--enable-preview</argLine>
|
||||
<reportFormat>plain</reportFormat>
|
||||
<consoleOutputReporter>
|
||||
<disable>true</disable>
|
||||
</consoleOutputReporter>
|
||||
<statelessTestsetInfoReporter
|
||||
implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
|
||||
</statelessTestsetInfoReporter>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.cryptomator.common;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -31,6 +32,7 @@ public class Environment {
|
||||
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";
|
||||
|
||||
private Environment() {}
|
||||
|
||||
@@ -43,15 +45,16 @@ public class Environment {
|
||||
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
|
||||
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
|
||||
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
|
||||
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
|
||||
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
|
||||
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
|
||||
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
|
||||
logCryptomatorSystemProperty(MOUNTPOINT_DIR_PROP_NAME);
|
||||
logCryptomatorSystemProperty(MIN_PW_LENGTH_PROP_NAME);
|
||||
logCryptomatorSystemProperty(APP_VERSION_PROP_NAME);
|
||||
logCryptomatorSystemProperty(BUILD_NUMBER_PROP_NAME);
|
||||
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
|
||||
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
|
||||
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
|
||||
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME);
|
||||
}
|
||||
|
||||
public static Environment getInstance() {
|
||||
@@ -74,10 +77,6 @@ public class Environment {
|
||||
return getPaths(SETTINGS_PATH_PROP_NAME);
|
||||
}
|
||||
|
||||
public Stream<Path> getP12Path() {
|
||||
return getPaths(P12_PATH_PROP_NAME);
|
||||
}
|
||||
|
||||
public Stream<Path> getIpcSocketPath() {
|
||||
return getPaths(IPC_SOCKET_PATH_PROP_NAME);
|
||||
}
|
||||
@@ -86,6 +85,10 @@ public class Environment {
|
||||
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
|
||||
}
|
||||
|
||||
public Stream<Path> getP12Path() {
|
||||
return getPaths(P12_PATH_PROP_NAME);
|
||||
}
|
||||
|
||||
public Optional<Path> getLogDir() {
|
||||
return getPath(LOG_DIR_PROP_NAME);
|
||||
}
|
||||
@@ -94,14 +97,14 @@ public class Environment {
|
||||
return Optional.ofNullable(System.getProperty(LOOPBACK_ALIAS_PROP_NAME));
|
||||
}
|
||||
|
||||
public Optional<Path> getPluginDir() {
|
||||
return getPath(PLUGIN_DIR_PROP_NAME);
|
||||
}
|
||||
|
||||
public Optional<Path> getMountPointsDir() {
|
||||
return getPath(MOUNTPOINT_DIR_PROP_NAME);
|
||||
}
|
||||
|
||||
public int getMinPwLength() {
|
||||
return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the app version defined in the {@value APP_VERSION_PROP_NAME} property or returns "SNAPSHOT".
|
||||
*
|
||||
@@ -115,20 +118,24 @@ public class Environment {
|
||||
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
|
||||
}
|
||||
|
||||
public int getMinPwLength() {
|
||||
return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
|
||||
public Optional<Path> getPluginDir() {
|
||||
return getPath(PLUGIN_DIR_PROP_NAME);
|
||||
}
|
||||
|
||||
public boolean showTrayIcon() {
|
||||
return Boolean.getBoolean(TRAY_ICON_PROP_NAME);
|
||||
}
|
||||
|
||||
public boolean disableUpdateCheck() {
|
||||
return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME);
|
||||
}
|
||||
|
||||
private Optional<Path> getPath(String propertyName) {
|
||||
String value = System.getProperty(propertyName);
|
||||
return Optional.ofNullable(value).map(Paths::get);
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
@VisibleForTesting
|
||||
Stream<Path> getPaths(String propertyName) {
|
||||
Stream<String> rawSettingsPaths = getRawList(propertyName, System.getProperty("path.separator").charAt(0));
|
||||
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Path::of);
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.cryptomator.common;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.base.Throwables;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
@@ -114,7 +115,7 @@ public class ErrorCode {
|
||||
* @param bottomFrames Other stack frames, potentially forming the bottom of the stack of <code>allFrames</code>
|
||||
* @return The number of additional frames in <code>allFrames</code>. In most cases this should be equal to the difference in size.
|
||||
*/
|
||||
// visible for testing
|
||||
@VisibleForTesting
|
||||
static int countTopmostFrames(StackTraceElement[] allFrames, StackTraceElement[] bottomFrames) {
|
||||
if (allFrames.length < bottomFrames.length) {
|
||||
// if frames had been stacked on top of bottomFrames, allFrames would be larger
|
||||
@@ -124,7 +125,7 @@ public class ErrorCode {
|
||||
}
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
@VisibleForTesting
|
||||
static <T> int commonSuffixLength(T[] set, T[] subset) {
|
||||
Preconditions.checkArgument(set.length >= subset.length);
|
||||
// iterate items backwards as long as they are identical
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ public final class OneDriveWindowsLocationPresetsProvider implements LocationPre
|
||||
ProcessBuilder command = new ProcessBuilder(args);
|
||||
Process p = command.start();
|
||||
waitForSuccess(p, 3, "`reg query`");
|
||||
return p.inputReader(StandardCharsets.UTF_8).lines().filter(outputFilter);
|
||||
return p.inputReader(StandardCharsets.ISO_8859_1).lines().filter(outputFilter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.cryptomator.common.mount;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -66,7 +67,7 @@ public final class MountWithinParentUtil {
|
||||
}
|
||||
}
|
||||
|
||||
//visible for testing
|
||||
@VisibleForTesting
|
||||
static MountPointState getMountPointState(Path path) throws IOException, IllegalMountPointException {
|
||||
if (Files.notExists(path, LinkOption.NOFOLLOW_LINKS)) {
|
||||
return MountPointState.NOT_EXISTING;
|
||||
@@ -82,7 +83,7 @@ public final class MountWithinParentUtil {
|
||||
return MountPointState.BROKEN_JUNCTION;
|
||||
}
|
||||
|
||||
//visible for testing
|
||||
@VisibleForTesting
|
||||
enum MountPointState {
|
||||
|
||||
NOT_EXISTING,
|
||||
@@ -93,7 +94,7 @@ public final class MountWithinParentUtil {
|
||||
|
||||
}
|
||||
|
||||
//visible for testing
|
||||
@VisibleForTesting
|
||||
static void removeResidualHideaway(Path mountPoint, Path hideaway) throws IOException {
|
||||
checkIsHideawayDirectory(mountPoint, hideaway);
|
||||
Files.delete(hideaway); //Fails if not empty
|
||||
@@ -155,7 +156,7 @@ public final class MountWithinParentUtil {
|
||||
}
|
||||
}
|
||||
|
||||
//visible for testing
|
||||
@VisibleForTesting
|
||||
static Path getHideaway(Path mountPoint) {
|
||||
return mountPoint.resolveSibling(HIDEAWAY_PREFIX + mountPoint.getFileName().toString() + HIDEAWAY_SUFFIX);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,6 @@ public class Settings {
|
||||
public final IntegerProperty windowYPosition;
|
||||
public final IntegerProperty windowWidth;
|
||||
public final IntegerProperty windowHeight;
|
||||
public final StringProperty displayConfiguration;
|
||||
public final StringProperty language;
|
||||
public final StringProperty mountService;
|
||||
public final StringProperty lastUpdateCheck;
|
||||
@@ -103,7 +102,6 @@ public class Settings {
|
||||
this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition);
|
||||
this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth);
|
||||
this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
|
||||
this.displayConfiguration = new SimpleStringProperty(this, "displayConfiguration", json.displayConfiguration);
|
||||
this.language = new SimpleStringProperty(this, "language", json.language);
|
||||
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
||||
this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck);
|
||||
@@ -131,7 +129,6 @@ public class Settings {
|
||||
windowYPosition.addListener(this::somethingChanged);
|
||||
windowWidth.addListener(this::somethingChanged);
|
||||
windowHeight.addListener(this::somethingChanged);
|
||||
displayConfiguration.addListener(this::somethingChanged);
|
||||
language.addListener(this::somethingChanged);
|
||||
mountService.addListener(this::somethingChanged);
|
||||
lastUpdateCheck.addListener(this::somethingChanged);
|
||||
@@ -186,7 +183,6 @@ public class Settings {
|
||||
json.windowYPosition = windowYPosition.get();
|
||||
json.windowWidth = windowWidth.get();
|
||||
json.windowHeight = windowHeight.get();
|
||||
json.displayConfiguration = displayConfiguration.get();
|
||||
json.language = language.get();
|
||||
json.mountService = mountService.get();
|
||||
json.lastUpdateCheck = lastUpdateCheck.get();
|
||||
|
||||
@@ -31,9 +31,6 @@ class SettingsJson {
|
||||
@JsonProperty("theme")
|
||||
UiTheme theme = Settings.DEFAULT_THEME;
|
||||
|
||||
@JsonProperty("displayConfiguration")
|
||||
String displayConfiguration;
|
||||
|
||||
@JsonProperty("keychainProvider")
|
||||
String keychainProvider = Settings.DEFAULT_KEYCHAIN_PROVIDER;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
@@ -126,7 +127,7 @@ public class VaultSettings {
|
||||
return json;
|
||||
}
|
||||
|
||||
//visible for testing
|
||||
@VisibleForTesting
|
||||
static String normalizeDisplayName(String original) {
|
||||
if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
|
||||
return "_";
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -48,7 +49,7 @@ class FileOpenRequestHandler {
|
||||
handleLaunchArgs(FileSystems.getDefault(), args);
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
@VisibleForTesting
|
||||
void handleLaunchArgs(FileSystem fs, List<String> args) {
|
||||
Collection<Path> pathsToOpen = args.stream().map(str -> {
|
||||
try {
|
||||
|
||||
@@ -5,6 +5,7 @@ import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
|
||||
import ch.qos.logback.classic.spi.Configurator;
|
||||
import ch.qos.logback.classic.spi.ConfiguratorRank;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.Appender;
|
||||
import ch.qos.logback.core.ConsoleAppender;
|
||||
@@ -19,6 +20,7 @@ import org.cryptomator.common.Environment;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
@ConfiguratorRank(ConfiguratorRank.CUSTOM_NORMAL_PRIORITY)
|
||||
public class LogbackConfigurator extends ContextAwareBase implements Configurator {
|
||||
|
||||
private static final String LOG_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";
|
||||
|
||||
@@ -45,9 +45,8 @@ public abstract class AddVaultModule {
|
||||
@Provides
|
||||
@AddVaultWizardWindow
|
||||
@AddVaultWizardScoped
|
||||
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
|
||||
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
|
||||
stage.setResizable(false);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.initOwner(primaryStage);
|
||||
@@ -90,13 +89,6 @@ public abstract class AddVaultModule {
|
||||
|
||||
// ------------------
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.ADDVAULT_WELCOME)
|
||||
@AddVaultWizardScoped
|
||||
static Scene provideWelcomeScene(@AddVaultWizardWindow FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_WELCOME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.ADDVAULT_EXISTING)
|
||||
@AddVaultWizardScoped
|
||||
@@ -148,11 +140,6 @@ public abstract class AddVaultModule {
|
||||
|
||||
// ------------------
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(AddVaultWelcomeController.class)
|
||||
abstract FxController bindWelcomeController(AddVaultWelcomeController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(ChooseExistingVaultController.class)
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package org.cryptomator.ui.addvaultwizard;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@AddVaultWizardScoped
|
||||
public class AddVaultWelcomeController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AddVaultWelcomeController.class);
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> chooseExistingVaultScene;
|
||||
private final Lazy<Scene> createNewVaultScene;
|
||||
|
||||
@Inject
|
||||
AddVaultWelcomeController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_EXISTING) Lazy<Scene> chooseExistingVaultScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> createNewVaultScene) {
|
||||
this.window = window;
|
||||
this.chooseExistingVaultScene = chooseExistingVaultScene;
|
||||
this.createNewVaultScene = createNewVaultScene;
|
||||
}
|
||||
|
||||
public void createNewVault() {
|
||||
LOG.debug("AddVaultWelcomeController.createNewVault()");
|
||||
window.setScene(createNewVaultScene.get());
|
||||
}
|
||||
|
||||
public void chooseExistingVault() {
|
||||
LOG.debug("AddVaultWelcomeController.chooseExistingVault()");
|
||||
window.setScene(chooseExistingVaultScene.get());
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import org.cryptomator.ui.common.FxmlScene;
|
||||
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@AddVaultWizardScoped
|
||||
@Subcomponent(modules = {AddVaultModule.class})
|
||||
@@ -20,12 +21,23 @@ public interface AddVaultWizardComponent {
|
||||
@AddVaultWizardWindow
|
||||
Stage window();
|
||||
|
||||
@FxmlScene(FxmlFile.ADDVAULT_WELCOME)
|
||||
Lazy<Scene> scene();
|
||||
@FxmlScene(FxmlFile.ADDVAULT_NEW_NAME)
|
||||
Lazy<Scene> sceneNew();
|
||||
@FxmlScene(FxmlFile.ADDVAULT_EXISTING)
|
||||
Lazy<Scene> sceneExisting();
|
||||
|
||||
default void showAddVaultWizard() {
|
||||
default void showAddNewVaultWizard(ResourceBundle resourceBundle) {
|
||||
Stage stage = window();
|
||||
stage.setScene(scene().get());
|
||||
stage.setScene(sceneNew().get());
|
||||
stage.setTitle(resourceBundle.getString("addvaultwizard.new.title"));
|
||||
stage.sizeToScene();
|
||||
stage.show();
|
||||
}
|
||||
|
||||
default void showAddExistingVaultWizard(ResourceBundle resourceBundle) {
|
||||
Stage stage = window();
|
||||
stage.setScene(sceneExisting().get());
|
||||
stage.setTitle(resourceBundle.getString("addvaultwizard.existing.title"));
|
||||
stage.sizeToScene();
|
||||
stage.show();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ public class ChooseExistingVaultController implements FxController {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ChooseExistingVaultController.class);
|
||||
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> welcomeScene;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final ObjectProperty<Path> vaultPath;
|
||||
@@ -45,9 +44,15 @@ public class ChooseExistingVaultController implements FxController {
|
||||
private final ObservableValue<Image> screenshot;
|
||||
|
||||
@Inject
|
||||
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle, FxApplicationStyle applicationStyle) {
|
||||
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, //
|
||||
@FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, //
|
||||
FxApplicationWindows appWindows, //
|
||||
ObjectProperty<Path> vaultPath, //
|
||||
@AddVaultWizardWindow ObjectProperty<Vault> vault, //
|
||||
VaultListManager vaultListManager, //
|
||||
ResourceBundle resourceBundle, //
|
||||
FxApplicationStyle applicationStyle) {
|
||||
this.window = window;
|
||||
this.welcomeScene = welcomeScene;
|
||||
this.successScene = successScene;
|
||||
this.appWindows = appWindows;
|
||||
this.vaultPath = vaultPath;
|
||||
@@ -70,11 +75,6 @@ public class ChooseExistingVaultController implements FxController {
|
||||
return new Image((Objects.requireNonNull(getClass().getResource(imageResourcePath)).toString()));
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void back() {
|
||||
window.setScene(welcomeScene.get());
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void chooseFileAndNext() {
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
|
||||
@@ -17,7 +17,6 @@ import javafx.scene.Scene;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.stage.Stage;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@AddVaultWizardScoped
|
||||
@@ -27,16 +26,17 @@ public class CreateNewVaultNameController implements FxController {
|
||||
|
||||
public TextField textField;
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> welcomeScene;
|
||||
private final Lazy<Scene> chooseLocationScene;
|
||||
private final ObjectProperty<Path> vaultPath;
|
||||
private final StringProperty vaultName;
|
||||
private final BooleanBinding validVaultName;
|
||||
|
||||
@Inject
|
||||
CreateNewVaultNameController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
||||
CreateNewVaultNameController(@AddVaultWizardWindow Stage window, //
|
||||
@FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, //
|
||||
ObjectProperty<Path> vaultPath, //
|
||||
@Named("vaultName") StringProperty vaultName) {
|
||||
this.window = window;
|
||||
this.welcomeScene = welcomeScene;
|
||||
this.chooseLocationScene = chooseLocationScene;
|
||||
this.vaultPath = vaultPath;
|
||||
this.vaultName = vaultName;
|
||||
@@ -58,11 +58,6 @@ public class CreateNewVaultNameController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void back() {
|
||||
window.setScene(welcomeScene.get());
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void next() {
|
||||
window.setScene(chooseLocationScene.get());
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.cryptomator.ui.addvaultwizard;
|
||||
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
@@ -51,7 +53,7 @@ public class ReadmeGenerator {
|
||||
resourceBundle.getString("addvault.new.readme.accessLocation.4")));
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
@VisibleForTesting
|
||||
String createDocument(Iterable<String> paragraphs) {
|
||||
StringBuilder sb = new StringBuilder(RTF_HEADER);
|
||||
for (String p : paragraphs) {
|
||||
@@ -63,7 +65,7 @@ public class ReadmeGenerator {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
@VisibleForTesting
|
||||
String escapeNonAsciiChars(CharSequence input) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
appendEscaped(sb, input);
|
||||
|
||||
@@ -8,7 +8,6 @@ public enum FxmlFile {
|
||||
ADDVAULT_NEW_PASSWORD("/fxml/addvault_new_password.fxml"), //
|
||||
ADDVAULT_NEW_RECOVERYKEY("/fxml/addvault_new_recoverykey.fxml"), //
|
||||
ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), //
|
||||
ADDVAULT_WELCOME("/fxml/addvault_welcome.fxml"), //
|
||||
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
|
||||
CONVERTVAULT_HUBTOPASSWORD_START("/fxml/convertvault_hubtopassword_start.fxml"), //
|
||||
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
|
||||
@@ -21,10 +20,12 @@ public enum FxmlFile {
|
||||
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
|
||||
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
|
||||
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
|
||||
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
|
||||
HUB_LEGACY_REGISTER_DEVICE("/fxml/hub_legacy_register_device.fxml"), //
|
||||
HUB_REGISTER_SUCCESS("/fxml/hub_register_success.fxml"), //
|
||||
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"),
|
||||
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"), //
|
||||
HUB_SETUP_DEVICE("/fxml/hub_setup_device.fxml"), //
|
||||
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
|
||||
HUB_REQUIRE_ACCOUNT_INIT("/fxml/hub_require_account_init.fxml"), //
|
||||
LOCK_FORCED("/fxml/lock_forced.fxml"), //
|
||||
LOCK_FAILED("/fxml/lock_failed.fxml"), //
|
||||
MAIN_WINDOW("/fxml/main_window.fxml"), //
|
||||
|
||||
@@ -53,6 +53,7 @@ public enum FontAwesome5Icon {
|
||||
TIMES("\uF00D"), //
|
||||
TRASH("\uF1F8"), //
|
||||
UNLINK("\uf127"), //
|
||||
USER_COG("\uf4fe"), //
|
||||
WRENCH("\uF0AD"), //
|
||||
WINDOW_MINIMIZE("\uF2D1"), //
|
||||
;
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -116,7 +117,7 @@ public class HubToPasswordConvertController implements FxController {
|
||||
}, Platform::runLater); //
|
||||
}
|
||||
|
||||
//visible for testing
|
||||
@VisibleForTesting
|
||||
void convertInternal() throws CompletionException, IllegalArgumentException {
|
||||
var passphrase = newPasswordController.getNewPassword();
|
||||
var vaultPath = vault.getPath();
|
||||
@@ -141,7 +142,7 @@ public class HubToPasswordConvertController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
//visible for testing
|
||||
@VisibleForTesting
|
||||
void backupHubConfig(Path hubConfigPath) throws IOException {
|
||||
byte[] hubConfigBytes = Files.readAllBytes(hubConfigPath);
|
||||
Path backupPath = hubConfigPath.resolveSibling(VAULTCONFIG_FILENAME + BackupHelper.generateFileIdSuffix(hubConfigBytes) + MASTERKEY_BACKUP_SUFFIX);
|
||||
@@ -149,7 +150,7 @@ public class HubToPasswordConvertController implements FxController {
|
||||
LOG.debug("Successfully created hub config backup {}", backupPath.getFileName());
|
||||
}
|
||||
|
||||
//visible for testing
|
||||
@VisibleForTesting
|
||||
Path createPasswordConfig(Path passwordConfigPath, Path masterkeyFile, Passphrase passphrase) throws IOException, MasterkeyLoadingFailedException {
|
||||
var unverifiedVaultConfig = vault.getVaultConfigCache().get();
|
||||
try (var masterkey = masterkeyFileAccess.load(masterkeyFile, passphrase)) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -42,19 +43,21 @@ public class ErrorController implements FxController {
|
||||
|
||||
private static final ObjectMapper JSON = new ObjectMapper();
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ErrorController.class);
|
||||
private static final String ERROR_CODES_URL = "https://api.cryptomator.org/desktop/error-codes.json";
|
||||
private static final String USER_AGENT_FORMAT = "Cryptomator/%s (Build %s) (%s %s %s)";
|
||||
private static final String ERROR_CODES_URL_FORMAT = "https://api.cryptomator.org/desktop/error-codes.json?error-code=%s";
|
||||
private static final String SEARCH_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/categories/errors?discussions_q=category:Errors+%s";
|
||||
private static final String REPORT_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/new?category=Errors&title=Error+%s&body=%s";
|
||||
private static final String SEARCH_ERRORCODE_DELIM = " OR ";
|
||||
private static final String REPORT_BODY_TEMPLATE = """
|
||||
<!-- 💚 Thank you for reporting this error. -->
|
||||
OS: %s / %s
|
||||
App: %s / %s
|
||||
|
||||
<!-- ✏ Please describe what happened as accurately as possible. -->
|
||||
|
||||
<!-- 📋 Please also copy and paste the detail text from the error window. -->
|
||||
|
||||
<!-- ℹ Text enclosed like this (chevrons, exclamation mark, two dashes) is not visible to others! -->
|
||||
Description:
|
||||
|
||||
<!-- 📋 Please also copy and paste the details from the error window. -->
|
||||
Details:
|
||||
|
||||
<!-- ❗ If the description or the detail text is missing, the discussion will be deleted. -->
|
||||
""";
|
||||
@@ -141,11 +144,19 @@ public class ErrorController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void lookUpSolution() {
|
||||
String userAgent = USER_AGENT_FORMAT.formatted( //
|
||||
environment.getAppVersion(), //
|
||||
environment.getBuildNumber().orElse("undefined"), //
|
||||
System.getProperty("os.name"), //
|
||||
System.getProperty("os.version"), //
|
||||
System.getProperty("os.arch"));
|
||||
isLoadingHttpResponse.set(true);
|
||||
askedForLookupDatabasePermission.set(true);
|
||||
HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
|
||||
HttpRequest httpRequest = HttpRequest.newBuilder()//
|
||||
.uri(URI.create(ERROR_CODES_URL))//
|
||||
.header("User-Agent", userAgent)
|
||||
.timeout(Duration.ofSeconds(10))
|
||||
.uri(URI.create(ERROR_CODES_URL_FORMAT.formatted(URLEncoder.encode(errorCode.toString(),StandardCharsets.UTF_8))))//
|
||||
.build();
|
||||
httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofInputStream())//
|
||||
.thenAcceptAsync(this::loadHttpResponse, executorService)//
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||
import org.slf4j.Logger;
|
||||
@@ -17,6 +18,7 @@ public class FxApplication {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
||||
|
||||
private final long startupTime;
|
||||
private final Environment environment;
|
||||
private final Settings settings;
|
||||
private final AppLaunchEventHandler launchEventHandler;
|
||||
private final Lazy<TrayMenuComponent> trayMenu;
|
||||
@@ -26,8 +28,9 @@ public class FxApplication {
|
||||
private final AutoUnlocker autoUnlocker;
|
||||
|
||||
@Inject
|
||||
FxApplication(@Named("startupTime") long startupTime, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
|
||||
FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
|
||||
this.startupTime = startupTime;
|
||||
this.environment = environment;
|
||||
this.settings = settings;
|
||||
this.launchEventHandler = launchEventHandler;
|
||||
this.trayMenu = trayMenu;
|
||||
@@ -68,7 +71,9 @@ public class FxApplication {
|
||||
return null;
|
||||
});
|
||||
|
||||
appWindows.checkAndShowUpdateReminderWindow();
|
||||
if (!environment.disableUpdateCheck()) {
|
||||
appWindows.checkAndShowUpdateReminderWindow();
|
||||
}
|
||||
|
||||
launchEventHandler.startHandlingLaunchEvents();
|
||||
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
|
||||
|
||||
@@ -22,23 +22,23 @@ public class UpdateChecker {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class);
|
||||
private static final Duration AUTOCHECK_DELAY = Duration.seconds(5);
|
||||
|
||||
private final Environment env;
|
||||
private final Settings settings;
|
||||
private final String currentVersion;
|
||||
private final StringProperty latestVersionProperty;
|
||||
private final Comparator<String> semVerComparator;
|
||||
private final ScheduledService<String> updateCheckerService;
|
||||
|
||||
@Inject
|
||||
UpdateChecker(Settings settings, Environment env, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator<String> semVerComparator, ScheduledService<String> updateCheckerService) {
|
||||
this.env = env;
|
||||
this.settings = settings;
|
||||
this.latestVersionProperty = latestVersionProperty;
|
||||
this.semVerComparator = semVerComparator;
|
||||
this.updateCheckerService = updateCheckerService;
|
||||
this.currentVersion = env.getAppVersion();
|
||||
}
|
||||
|
||||
public void automaticallyCheckForUpdatesIfEnabled() {
|
||||
if (settings.checkForUpdates.get()) {
|
||||
if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) {
|
||||
startCheckingForUpdates(AUTOCHECK_DELAY);
|
||||
}
|
||||
}
|
||||
@@ -63,9 +63,9 @@ public class UpdateChecker {
|
||||
|
||||
private void checkSucceeded(WorkerStateEvent event) {
|
||||
String latestVersion = updateCheckerService.getValue();
|
||||
LOG.info("Current version: {}, lastest version: {}", currentVersion, latestVersion);
|
||||
LOG.info("Current version: {}, lastest version: {}", getCurrentVersion(), latestVersion);
|
||||
|
||||
if (semVerComparator.compare(currentVersion, latestVersion) < 0) {
|
||||
if (semVerComparator.compare(getCurrentVersion(), latestVersion) < 0) {
|
||||
// update is available
|
||||
latestVersionProperty.set(latestVersion);
|
||||
} else {
|
||||
@@ -88,7 +88,7 @@ public class UpdateChecker {
|
||||
}
|
||||
|
||||
public String getCurrentVersion() {
|
||||
return currentVersion;
|
||||
return env.getAppVersion();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ public abstract class UpdateCheckerModule {
|
||||
return HttpRequest.newBuilder() //
|
||||
.uri(LATEST_VERSION_URI) //
|
||||
.header("User-Agent", userAgent) //
|
||||
.timeout(java.time.Duration.ofSeconds(10))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -101,16 +101,16 @@ public class StartController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadingKeyFailed(Throwable e) {
|
||||
switch (e) {
|
||||
case UnlockCancelledException uce -> {} //ok
|
||||
case VaultKeyInvalidException vkie -> {
|
||||
LOG.error("Invalid key"); //TODO: specific error screen
|
||||
private void loadingKeyFailed(Throwable t) {
|
||||
switch (t) {
|
||||
case UnlockCancelledException e -> {} // ok // TODO: rename to _ with JEP 443
|
||||
case VaultKeyInvalidException e -> { // TODO: rename to _ with JEP 443
|
||||
LOG.error("Invalid key"); // TODO: specific error screen
|
||||
appWindows.showErrorWindow(e, window, null);
|
||||
}
|
||||
default -> {
|
||||
LOG.error("Failed to load key.", e);
|
||||
appWindows.showErrorWindow(e, window, null);
|
||||
LOG.error("Failed to load key.", t);
|
||||
appWindows.showErrorWindow(t, window, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,13 +35,13 @@ public class AuthFlowController implements FxController {
|
||||
private final String deviceId;
|
||||
private final HubConfig hubConfig;
|
||||
private final AtomicReference<String> tokenRef;
|
||||
private final CompletableFuture<JWEObject> result;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
private final Lazy<Scene> receiveKeyScene;
|
||||
private final ObjectProperty<URI> authUri;
|
||||
private AuthFlowTask task;
|
||||
|
||||
@Inject
|
||||
public AuthFlowController(Application application, @KeyLoading Stage window, ExecutorService executor, @Named("deviceId") String deviceId, HubConfig hubConfig, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<JWEObject> result, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy<Scene> receiveKeyScene) {
|
||||
public AuthFlowController(Application application, @KeyLoading Stage window, ExecutorService executor, @Named("deviceId") String deviceId, HubConfig hubConfig, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy<Scene> receiveKeyScene) {
|
||||
this.application = application;
|
||||
this.window = window;
|
||||
this.executor = executor;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.github.coffeelibs.tinyoauth2client.AuthFlow;
|
||||
import io.github.coffeelibs.tinyoauth2client.TinyOAuth2;
|
||||
import io.github.coffeelibs.tinyoauth2client.http.response.Response;
|
||||
|
||||
import javafx.concurrent.Task;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.time.Duration;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class AuthFlowTask extends Task<String> {
|
||||
@@ -21,7 +22,7 @@ class AuthFlowTask extends Task<String> {
|
||||
/**
|
||||
* Spawns a server and waits for the redirectUri to be called.
|
||||
*
|
||||
* @param hubConfig Configuration object holding parameters required by {@link AuthFlow}
|
||||
* @param hubConfig Configuration object holding parameters required by {@link io.github.coffeelibs.tinyoauth2client.AuthorizationCodeGrant}
|
||||
* @param redirectUriConsumer A callback invoked with the redirectUri, as soon as the server has started
|
||||
*/
|
||||
public AuthFlowTask(HubConfig hubConfig, AuthFlowContext authFlowContext, Consumer<URI> redirectUriConsumer) {
|
||||
@@ -34,10 +35,11 @@ class AuthFlowTask extends Task<String> {
|
||||
protected String call() throws IOException, InterruptedException {
|
||||
var response = TinyOAuth2.client(hubConfig.clientId) //
|
||||
.withTokenEndpoint(URI.create(hubConfig.tokenEndpoint)) //
|
||||
.authFlow(URI.create(hubConfig.authEndpoint)) //
|
||||
.withRequestTimeout(Duration.ofSeconds(10)) //
|
||||
.authorizationCodeGrant(URI.create(hubConfig.authEndpoint)) //
|
||||
.setSuccessResponse(Response.redirect(URI.create(hubConfig.authSuccessUrl + "&device=" + authFlowContext.deviceId()))) //
|
||||
.setErrorResponse(Response.redirect(URI.create(hubConfig.authErrorUrl + "&device=" + authFlowContext.deviceId()))) //
|
||||
.authorize(redirectUriConsumer);
|
||||
.authorize(HttpClient.newHttpClient(), redirectUriConsumer);
|
||||
if (response.statusCode() != 200) {
|
||||
throw new NotOkResponseException("Authorization returned status code " + response.statusCode());
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
record CreateDeviceDto(String id, String name, String publicKey) {
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
class HttpHelper {
|
||||
|
||||
public static String readBody(HttpResponse<InputStream> response) throws IOException {
|
||||
try (var in = response.body(); var reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
||||
return CharStreams.toString(reader);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
// needs to be accessible by JSON decoder
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@@ -9,8 +13,24 @@ public class HubConfig {
|
||||
public String clientId;
|
||||
public String authEndpoint;
|
||||
public String tokenEndpoint;
|
||||
public String devicesResourceUrl;
|
||||
public String authSuccessUrl;
|
||||
public String authErrorUrl;
|
||||
public @Nullable String apiBaseUrl;
|
||||
@Deprecated // use apiBaseUrl + "/devices/"
|
||||
public String devicesResourceUrl;
|
||||
|
||||
public URI getApiBaseUrl() {
|
||||
if (apiBaseUrl != null) {
|
||||
// make sure to end on "/":
|
||||
return URI.create(apiBaseUrl + "/").normalize();
|
||||
} else { // legacy approach
|
||||
assert devicesResourceUrl != null;
|
||||
// make sure to end on "/":
|
||||
return URI.create(devicesResourceUrl + "/..").normalize();
|
||||
}
|
||||
}
|
||||
|
||||
public URI getWebappBaseUrl() {
|
||||
return getApiBaseUrl().resolve("../app/");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.nimbusds.jose.JWEObject;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -69,7 +68,7 @@ public abstract class HubKeyLoadingModule {
|
||||
|
||||
@Provides
|
||||
@KeyLoadingScoped
|
||||
static CompletableFuture<JWEObject> provideResult() {
|
||||
static CompletableFuture<ReceivedKey> provideResult() {
|
||||
return new CompletableFuture<>();
|
||||
}
|
||||
|
||||
@@ -114,12 +113,13 @@ public abstract class HubKeyLoadingModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE)
|
||||
@FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE)
|
||||
@KeyLoadingScoped
|
||||
static Scene provideHubRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_DEVICE);
|
||||
static Scene provideHubLegacyRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE);
|
||||
}
|
||||
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS)
|
||||
@KeyLoadingScoped
|
||||
@@ -134,6 +134,13 @@ public abstract class HubKeyLoadingModule {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_FAILED);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_SETUP_DEVICE)
|
||||
@KeyLoadingScoped
|
||||
static Scene provideHubRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_SETUP_DEVICE);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE)
|
||||
@KeyLoadingScoped
|
||||
@@ -141,6 +148,13 @@ public abstract class HubKeyLoadingModule {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT)
|
||||
@KeyLoadingScoped
|
||||
static Scene provideRequireAccountInitScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT);
|
||||
}
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(NoKeychainController.class)
|
||||
@@ -166,6 +180,11 @@ public abstract class HubKeyLoadingModule {
|
||||
@FxControllerKey(RegisterDeviceController.class)
|
||||
abstract FxController bindRegisterDeviceController(RegisterDeviceController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(LegacyRegisterDeviceController.class)
|
||||
abstract FxController bindLegacyRegisterDeviceController(LegacyRegisterDeviceController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(RegisterSuccessController.class)
|
||||
@@ -180,4 +199,9 @@ public abstract class HubKeyLoadingModule {
|
||||
@IntoMap
|
||||
@FxControllerKey(UnauthorizedDeviceController.class)
|
||||
abstract FxController bindUnauthorizedDeviceController(UnauthorizedDeviceController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(RequireAccountInitController.class)
|
||||
abstract FxController bindRequireAccountInitController(RequireAccountInitController controller);
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
|
||||
private final KeychainManager keychainManager;
|
||||
private final Lazy<Scene> authFlowScene;
|
||||
private final Lazy<Scene> noKeychainScene;
|
||||
private final CompletableFuture<JWEObject> result;
|
||||
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<JWEObject> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle) {
|
||||
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle) {
|
||||
this.window = window;
|
||||
this.keychainManager = keychainManager;
|
||||
window.setTitle(windowTitle);
|
||||
@@ -60,7 +60,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
|
||||
var keypair = deviceKey.get();
|
||||
showWindow(authFlowScene);
|
||||
var jwe = result.get();
|
||||
return JWEHelper.decrypt(jwe, keypair.getPrivate());
|
||||
return jwe.decryptMasterkey(keypair.getPrivate());
|
||||
} catch (NoKeychainAccessProviderException e) {
|
||||
showWindow(noKeychainScene);
|
||||
throw new UnlockCancelledException("Unlock canceled due to missing prerequisites", e);
|
||||
|
||||
@@ -2,35 +2,103 @@ package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.nimbusds.jose.EncryptionMethod;
|
||||
import com.nimbusds.jose.JOSEException;
|
||||
import com.nimbusds.jose.JWEAlgorithm;
|
||||
import com.nimbusds.jose.JWEHeader;
|
||||
import com.nimbusds.jose.JWEObject;
|
||||
import com.nimbusds.jose.Payload;
|
||||
import com.nimbusds.jose.crypto.ECDHDecrypter;
|
||||
import com.nimbusds.jose.crypto.ECDHEncrypter;
|
||||
import com.nimbusds.jose.crypto.PasswordBasedDecrypter;
|
||||
import com.nimbusds.jose.jwk.Curve;
|
||||
import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
|
||||
import com.nimbusds.jose.jwk.gen.JWKGenerator;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
class JWEHelper {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JWEHelper.class);
|
||||
private static final String JWE_PAYLOAD_MASTERKEY_FIELD = "key";
|
||||
private static final String JWE_PAYLOAD_KEY_FIELD = "key";
|
||||
private static final String EC_ALG = "EC";
|
||||
|
||||
private JWEHelper(){}
|
||||
|
||||
public static Masterkey decrypt(JWEObject jwe, ECPrivateKey privateKey) throws MasterkeyLoadingFailedException {
|
||||
public static JWEObject encryptUserKey(ECPrivateKey userKey, ECPublicKey deviceKey) {
|
||||
try {
|
||||
jwe.decrypt(new ECDHDecrypter(privateKey));
|
||||
return readKey(jwe);
|
||||
var encodedUserKey = Base64.getEncoder().encodeToString(userKey.getEncoded());
|
||||
var keyGen = new ECKeyGenerator(Curve.P_384);
|
||||
var ephemeralKeyPair = keyGen.generate();
|
||||
var header = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A256GCM).ephemeralPublicKey(ephemeralKeyPair.toPublicJWK()).build();
|
||||
var payload = new Payload(Map.of(JWE_PAYLOAD_KEY_FIELD, encodedUserKey));
|
||||
var jwe = new JWEObject(header, payload);
|
||||
jwe.encrypt(new ECDHEncrypter(deviceKey));
|
||||
return jwe;
|
||||
} catch (JOSEException e) {
|
||||
LOG.warn("Failed to decrypt JWE: {}", jwe);
|
||||
throw new MasterkeyLoadingFailedException("Failed to decrypt JWE", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Masterkey readKey(JWEObject jwe) throws MasterkeyLoadingFailedException {
|
||||
public static ECPrivateKey decryptUserKey(JWEObject jwe, String setupCode) throws InvalidJweKeyException {
|
||||
try {
|
||||
jwe.decrypt(new PasswordBasedDecrypter(setupCode));
|
||||
return decodeUserKey(jwe);
|
||||
} catch (JOSEException e) {
|
||||
throw new InvalidJweKeyException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ECPrivateKey decryptUserKey(JWEObject jwe, ECPrivateKey deviceKey) throws InvalidJweKeyException {
|
||||
try {
|
||||
jwe.decrypt(new ECDHDecrypter(deviceKey));
|
||||
return decodeUserKey(jwe);
|
||||
} catch (JOSEException e) {
|
||||
throw new InvalidJweKeyException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static ECPrivateKey decodeUserKey(JWEObject decryptedJwe) {
|
||||
try {
|
||||
var keySpec = readKey(decryptedJwe, JWE_PAYLOAD_KEY_FIELD, PKCS8EncodedKeySpec::new);
|
||||
var factory = KeyFactory.getInstance(EC_ALG);
|
||||
var privateKey = factory.generatePrivate(keySpec);
|
||||
if (privateKey instanceof ECPrivateKey ecPrivateKey) {
|
||||
return ecPrivateKey;
|
||||
} else {
|
||||
throw new IllegalStateException(EC_ALG + " key factory not generating ECPrivateKeys");
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(EC_ALG + " not supported");
|
||||
} catch (InvalidKeySpecException e) {
|
||||
LOG.warn("Unexpected JWE payload: {}", decryptedJwe.getPayload());
|
||||
throw new MasterkeyLoadingFailedException("Unexpected JWE payload", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Masterkey decryptVaultKey(JWEObject jwe, ECPrivateKey privateKey) throws InvalidJweKeyException {
|
||||
try {
|
||||
jwe.decrypt(new ECDHDecrypter(privateKey));
|
||||
return readKey(jwe, JWE_PAYLOAD_KEY_FIELD, Masterkey::new);
|
||||
} catch (JOSEException e) {
|
||||
throw new InvalidJweKeyException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T readKey(JWEObject jwe, String keyField, Function<byte[], T> rawKeyFactory) throws MasterkeyLoadingFailedException {
|
||||
Preconditions.checkArgument(jwe.getState() == JWEObject.State.DECRYPTED);
|
||||
var fields = jwe.getPayload().toJSONObject();
|
||||
if (fields == null) {
|
||||
@@ -39,11 +107,11 @@ class JWEHelper {
|
||||
}
|
||||
var keyBytes = new byte[0];
|
||||
try {
|
||||
if (fields.get(JWE_PAYLOAD_MASTERKEY_FIELD) instanceof String key) {
|
||||
if (fields.get(keyField) instanceof String key) {
|
||||
keyBytes = BaseEncoding.base64().decode(key);
|
||||
return new Masterkey(keyBytes);
|
||||
return rawKeyFactory.apply(keyBytes);
|
||||
} else {
|
||||
throw new IllegalArgumentException("JWE payload doesn't contain field " + JWE_PAYLOAD_MASTERKEY_FIELD);
|
||||
throw new IllegalArgumentException("JWE payload doesn't contain field " + keyField);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.error("Unexpected JWE payload: {}", jwe.getPayload());
|
||||
@@ -52,4 +120,11 @@ class JWEHelper {
|
||||
Arrays.fill(keyBytes, (byte) 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidJweKeyException extends MasterkeyLoadingFailedException {
|
||||
|
||||
public InvalidJweKeyException(Throwable cause) {
|
||||
super("Invalid key", cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.settings.DeviceKey;
|
||||
import org.cryptomator.cryptolib.common.P384KeyPair;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@KeyLoadingScoped
|
||||
public class LegacyRegisterDeviceController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LegacyRegisterDeviceController.class);
|
||||
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
|
||||
private static final List<Integer> EXPECTED_RESPONSE_CODES = List.of(201, 409);
|
||||
|
||||
private final Stage window;
|
||||
private final HubConfig hubConfig;
|
||||
private final String bearerToken;
|
||||
private final Lazy<Scene> registerSuccessScene;
|
||||
private final Lazy<Scene> registerFailedScene;
|
||||
private final String deviceId;
|
||||
private final P384KeyPair keyPair;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
private final DecodedJWT jwt;
|
||||
private final HttpClient httpClient;
|
||||
private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
|
||||
|
||||
public TextField deviceNameField;
|
||||
public Button registerBtn;
|
||||
|
||||
@Inject
|
||||
public LegacyRegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
|
||||
this.window = window;
|
||||
this.hubConfig = hubConfig;
|
||||
this.deviceId = deviceId;
|
||||
this.keyPair = Objects.requireNonNull(deviceKey.get());
|
||||
this.result = result;
|
||||
this.bearerToken = Objects.requireNonNull(bearerToken.get());
|
||||
this.registerSuccessScene = registerSuccessScene;
|
||||
this.registerFailedScene = registerFailedScene;
|
||||
this.jwt = JWT.decode(this.bearerToken);
|
||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
deviceNameField.setText(determineHostname());
|
||||
deviceNameField.textProperty().addListener(observable -> deviceNameAlreadyExists.set(false));
|
||||
}
|
||||
|
||||
private String determineHostname() {
|
||||
try {
|
||||
var hostName = InetAddress.getLocalHost().getHostName();
|
||||
return Objects.requireNonNullElse(hostName, "");
|
||||
} catch (IOException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void register() {
|
||||
deviceNameAlreadyExists.set(false);
|
||||
registerBtn.setContentDisplay(ContentDisplay.LEFT);
|
||||
registerBtn.setDisable(true);
|
||||
|
||||
var deviceUri = URI.create(hubConfig.devicesResourceUrl + deviceId);
|
||||
var deviceKey = keyPair.getPublic().getEncoded();
|
||||
var dto = new CreateDeviceDto();
|
||||
dto.id = deviceId;
|
||||
dto.name = deviceNameField.getText();
|
||||
dto.publicKey = Base64.getUrlEncoder().withoutPadding().encodeToString(deviceKey);
|
||||
var json = toJson(dto);
|
||||
var request = HttpRequest.newBuilder(deviceUri) //
|
||||
.PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
.header("Content-Type", "application/json") //
|
||||
.build();
|
||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding()) //
|
||||
.thenApply(response -> {
|
||||
if (EXPECTED_RESPONSE_CODES.contains(response.statusCode())) {
|
||||
return response;
|
||||
} else {
|
||||
throw new RuntimeException("Server answered with unexpected status code " + response.statusCode());
|
||||
}
|
||||
}).handleAsync((response, throwable) -> {
|
||||
if (response != null) {
|
||||
this.handleResponse(response);
|
||||
} else {
|
||||
this.registrationFailed(throwable);
|
||||
}
|
||||
return null;
|
||||
}, Platform::runLater);
|
||||
}
|
||||
|
||||
private String toJson(CreateDeviceDto dto) {
|
||||
try {
|
||||
return JSON.writer().writeValueAsString(dto);
|
||||
} catch (JacksonException e) {
|
||||
throw new IllegalStateException("Failed to serialize DTO", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleResponse(HttpResponse<Void> voidHttpResponse) {
|
||||
assert EXPECTED_RESPONSE_CODES.contains(voidHttpResponse.statusCode());
|
||||
|
||||
if (voidHttpResponse.statusCode() == 409) {
|
||||
deviceNameAlreadyExists.set(true);
|
||||
registerBtn.setContentDisplay(ContentDisplay.TEXT_ONLY);
|
||||
registerBtn.setDisable(false);
|
||||
} else {
|
||||
LOG.debug("Device registration for hub instance {} successful.", hubConfig.authSuccessUrl);
|
||||
window.setScene(registerSuccessScene.get());
|
||||
}
|
||||
}
|
||||
|
||||
private void registrationFailed(Throwable cause) {
|
||||
LOG.warn("Device registration failed.", cause);
|
||||
window.setScene(registerFailedScene.get());
|
||||
result.completeExceptionally(cause);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
window.close();
|
||||
}
|
||||
|
||||
private void windowClosed(WindowEvent windowEvent) {
|
||||
result.cancel(true);
|
||||
}
|
||||
|
||||
/* Getter */
|
||||
|
||||
public String getUserName() {
|
||||
return jwt.getClaim("email").asString();
|
||||
}
|
||||
|
||||
|
||||
//--- Getters & Setters
|
||||
|
||||
public BooleanProperty deviceNameAlreadyExistsProperty() {
|
||||
return deviceNameAlreadyExists;
|
||||
}
|
||||
|
||||
public boolean getDeviceNameAlreadyExists() {
|
||||
return deviceNameAlreadyExists.get();
|
||||
}
|
||||
|
||||
private static class CreateDeviceDto {
|
||||
public String id;
|
||||
public String name;
|
||||
public final String type = "DESKTOP";
|
||||
public String publicKey;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.nimbusds.jose.JWEObject;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
@@ -8,6 +11,9 @@ import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@@ -17,14 +23,16 @@ import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -33,26 +41,35 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
@KeyLoadingScoped
|
||||
public class ReceiveKeyController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReceiveKeyController.class);
|
||||
private static final String SCHEME_PREFIX = "hub+";
|
||||
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
|
||||
private static final Duration REQ_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
private final Stage window;
|
||||
private final HubConfig hubConfig;
|
||||
private final String deviceId;
|
||||
private final String bearerToken;
|
||||
private final CompletableFuture<JWEObject> result;
|
||||
private final Lazy<Scene> registerDeviceScene;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
private final Lazy<Scene> setupDeviceScene;
|
||||
private final Lazy<Scene> legacyRegisterDeviceScene;
|
||||
private final Lazy<Scene> unauthorizedScene;
|
||||
private final Lazy<Scene> accountInitializationScene;
|
||||
private final URI vaultBaseUri;
|
||||
private final Lazy<Scene> invalidLicenseScene;
|
||||
private final HttpClient httpClient;
|
||||
|
||||
@Inject
|
||||
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<JWEObject> result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
|
||||
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, HubConfig hubConfig, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_SETUP_DEVICE) Lazy<Scene> setupDeviceScene, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy<Scene> accountInitializationScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
|
||||
this.window = window;
|
||||
this.hubConfig = hubConfig;
|
||||
this.deviceId = deviceId;
|
||||
this.bearerToken = Objects.requireNonNull(tokenRef.get());
|
||||
this.result = result;
|
||||
this.registerDeviceScene = registerDeviceScene;
|
||||
this.setupDeviceScene = setupDeviceScene;
|
||||
this.legacyRegisterDeviceScene = legacyRegisterDeviceScene;
|
||||
this.unauthorizedScene = unauthorizedScene;
|
||||
this.accountInitializationScene = accountInitializationScene;
|
||||
this.vaultBaseUri = getVaultBaseUri(vault);
|
||||
this.invalidLicenseScene = invalidLicenseScene;
|
||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||
@@ -61,23 +78,121 @@ public class ReceiveKeyController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
var keyUri = appendPath(vaultBaseUri, "/keys/" + deviceId);
|
||||
var request = HttpRequest.newBuilder(keyUri) //
|
||||
requestVaultMasterkey();
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 1 (Request): GET vault key for this user
|
||||
*/
|
||||
private void requestVaultMasterkey() {
|
||||
var accessTokenUri = appendPath(vaultBaseUri, "/access-token");
|
||||
var request = HttpRequest.newBuilder(accessTokenUri) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
.GET() //
|
||||
.timeout(REQ_TIMEOUT) //
|
||||
.build();
|
||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) //
|
||||
.thenAcceptAsync(this::loadedExistingKey, Platform::runLater) //
|
||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.US_ASCII)) //
|
||||
.thenAcceptAsync(this::receivedVaultMasterkey, Platform::runLater) //
|
||||
.exceptionally(this::retrievalFailed);
|
||||
}
|
||||
|
||||
private void loadedExistingKey(HttpResponse<InputStream> response) {
|
||||
/**
|
||||
* STEP 1 (Response): GET vault key for this user
|
||||
*
|
||||
* @param response Response
|
||||
*/
|
||||
private void receivedVaultMasterkey(HttpResponse<String> response) {
|
||||
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
|
||||
switch (response.statusCode()) {
|
||||
case 200 -> requestUserKey(response.body());
|
||||
case 402 -> licenseExceeded();
|
||||
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access - TODO: add specific dialog?
|
||||
case 449 -> accountInitializationRequired();
|
||||
case 404 -> requestLegacyAccessToken();
|
||||
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 2 (Request): GET user key for this device
|
||||
*/
|
||||
private void requestUserKey(String encryptedVaultKey) {
|
||||
var deviceTokenUri = URI.create(hubConfig.getApiBaseUrl() + "/devices/" + deviceId);
|
||||
var request = HttpRequest.newBuilder(deviceTokenUri) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
.GET() //
|
||||
.timeout(REQ_TIMEOUT) //
|
||||
.build();
|
||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) //
|
||||
.thenAcceptAsync(response -> receivedUserKey(encryptedVaultKey, response), Platform::runLater) //
|
||||
.exceptionally(this::retrievalFailed);
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 2 (Response): GET user key for this device
|
||||
*
|
||||
* @param response Response
|
||||
*/
|
||||
private void receivedUserKey(String encryptedVaultKey, HttpResponse<String> response) {
|
||||
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
|
||||
try {
|
||||
switch (response.statusCode()) {
|
||||
case 200 -> retrievalSucceeded(response);
|
||||
case 200 -> {
|
||||
var device = JSON.reader().readValue(response.body(), DeviceDto.class);
|
||||
receivedBothEncryptedKeys(encryptedVaultKey, device.userPrivateKey);
|
||||
}
|
||||
case 404 -> needsDeviceSetup(); // TODO: using the setup code, we can theoretically immediately unlock
|
||||
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void needsDeviceSetup() {
|
||||
window.setScene(setupDeviceScene.get());
|
||||
}
|
||||
|
||||
private void receivedBothEncryptedKeys(String encryptedVaultKey, String encryptedUserKey) throws IOException {
|
||||
try {
|
||||
var vaultKeyJwe = JWEObject.parse(encryptedVaultKey);
|
||||
var userKeyJwe = JWEObject.parse(encryptedUserKey);
|
||||
result.complete(ReceivedKey.vaultKeyAndUserKey(vaultKeyJwe, userKeyJwe));
|
||||
window.close();
|
||||
} catch (ParseException e) {
|
||||
throw new IOException("Failed to parse JWE", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LEGACY FALLBACK (Request): GET the legacy access token from Hub 1.x
|
||||
*/
|
||||
@Deprecated
|
||||
private void requestLegacyAccessToken() {
|
||||
var legacyAccessTokenUri = appendPath(vaultBaseUri, "/keys/" + deviceId);
|
||||
var request = HttpRequest.newBuilder(legacyAccessTokenUri) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
.GET() //
|
||||
.timeout(REQ_TIMEOUT) //
|
||||
.build();
|
||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.US_ASCII)) //
|
||||
.thenAcceptAsync(this::receivedLegacyAccessTokenResponse, Platform::runLater) //
|
||||
.exceptionally(this::retrievalFailed);
|
||||
}
|
||||
|
||||
/**
|
||||
* LEGACY FALLBACK (Response)
|
||||
*
|
||||
* @param response Response
|
||||
*/
|
||||
@Deprecated
|
||||
private void receivedLegacyAccessTokenResponse(HttpResponse<String> response) {
|
||||
try {
|
||||
switch (response.statusCode()) {
|
||||
case 200 -> receivedLegacyAccessTokenSuccess(response.body());
|
||||
case 402 -> licenseExceeded();
|
||||
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access
|
||||
case 404 -> needsDeviceRegistration();
|
||||
case 404 -> needsLegacyDeviceRegistration();
|
||||
default -> throw new IOException("Unexpected response " + response.statusCode());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
@@ -85,10 +200,11 @@ public class ReceiveKeyController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
private void retrievalSucceeded(HttpResponse<InputStream> response) throws IOException {
|
||||
@Deprecated
|
||||
private void receivedLegacyAccessTokenSuccess(String rawToken) throws IOException {
|
||||
try {
|
||||
var string = HttpHelper.readBody(response);
|
||||
result.complete(JWEObject.parse(string));
|
||||
var token = JWEObject.parse(rawToken);
|
||||
result.complete(ReceivedKey.legacyDeviceKey(token));
|
||||
window.close();
|
||||
} catch (ParseException e) {
|
||||
throw new IOException("Failed to parse JWE", e);
|
||||
@@ -99,14 +215,19 @@ public class ReceiveKeyController implements FxController {
|
||||
window.setScene(invalidLicenseScene.get());
|
||||
}
|
||||
|
||||
private void needsDeviceRegistration() {
|
||||
window.setScene(registerDeviceScene.get());
|
||||
@Deprecated
|
||||
private void needsLegacyDeviceRegistration() {
|
||||
window.setScene(legacyRegisterDeviceScene.get());
|
||||
}
|
||||
|
||||
private void accessNotGranted() {
|
||||
window.setScene(unauthorizedScene.get());
|
||||
}
|
||||
|
||||
private void accountInitializationRequired() {
|
||||
window.setScene(accountInitializationScene.get());
|
||||
}
|
||||
|
||||
private Void retrievalFailed(Throwable cause) {
|
||||
result.completeExceptionally(cause);
|
||||
return null;
|
||||
@@ -132,14 +253,17 @@ public class ReceiveKeyController implements FxController {
|
||||
|
||||
private static URI getVaultBaseUri(Vault vault) {
|
||||
try {
|
||||
var kid = vault.getVaultConfigCache().get().getKeyId();
|
||||
assert kid.getScheme().startsWith(SCHEME_PREFIX);
|
||||
var hubUriScheme = kid.getScheme().substring(SCHEME_PREFIX.length());
|
||||
return new URI(hubUriScheme, kid.getSchemeSpecificPart(), kid.getFragment());
|
||||
var url = vault.getVaultConfigCache().get().getKeyId();
|
||||
assert url.getScheme().startsWith(SCHEME_PREFIX);
|
||||
var correctedScheme = url.getScheme().substring(SCHEME_PREFIX.length());
|
||||
return new URI(correctedScheme, url.getSchemeSpecificPart(), url.getFragment());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalStateException("URI constructed from params known to be valid", e);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private record DeviceDto(@JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.nimbusds.jose.JWEObject;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
|
||||
@FunctionalInterface
|
||||
interface ReceivedKey {
|
||||
|
||||
/**
|
||||
* Decrypts the vault key.
|
||||
*
|
||||
* @param deviceKey This device's private key.
|
||||
* @return The decrypted vault key
|
||||
*/
|
||||
Masterkey decryptMasterkey(ECPrivateKey deviceKey);
|
||||
|
||||
/**
|
||||
* Creates an unlock response object from the user key + vault key.
|
||||
*
|
||||
* @param vaultKeyJwe a JWE containing the symmetric vault key, encrypted for this device's user.
|
||||
* @param userKeyJwe a JWE containing the user's private key, encrypted for this device.
|
||||
* @return Ciphertext received by Hub, which can be decrypted using this device's private key.
|
||||
*/
|
||||
static ReceivedKey vaultKeyAndUserKey(JWEObject vaultKeyJwe, JWEObject userKeyJwe) {
|
||||
return deviceKey -> {
|
||||
var userKey = JWEHelper.decryptUserKey(userKeyJwe, deviceKey);
|
||||
return JWEHelper.decryptVaultKey(vaultKeyJwe, userKey);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unlock response object from the received legacy "access token" JWE.
|
||||
*
|
||||
* @param vaultKeyJwe a JWE containing the symmetric vault key, encrypted for this device.
|
||||
* @return Ciphertext received by Hub, which can be decrypted using this device's private key.
|
||||
* @deprecated Only for compatibility with Hub 1.0 - 1.2
|
||||
*/
|
||||
@Deprecated
|
||||
static ReceivedKey legacyDeviceKey(JWEObject vaultKeyJwe) {
|
||||
return deviceKey -> JWEHelper.decryptVaultKey(vaultKeyJwe, deviceKey);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
@@ -20,6 +20,7 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -31,14 +32,16 @@ import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.text.ParseException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@@ -47,7 +50,7 @@ public class RegisterDeviceController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RegisterDeviceController.class);
|
||||
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
|
||||
private static final List<Integer> EXPECTED_RESPONSE_CODES = List.of(201, 409);
|
||||
private static final Duration REQ_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
private final Stage window;
|
||||
private final HubConfig hubConfig;
|
||||
@@ -55,26 +58,27 @@ public class RegisterDeviceController implements FxController {
|
||||
private final Lazy<Scene> registerSuccessScene;
|
||||
private final Lazy<Scene> registerFailedScene;
|
||||
private final String deviceId;
|
||||
private final P384KeyPair keyPair;
|
||||
private final CompletableFuture<JWEObject> result;
|
||||
private final DecodedJWT jwt;
|
||||
private final P384KeyPair deviceKeyPair;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
private final HttpClient httpClient;
|
||||
private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
|
||||
|
||||
private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
|
||||
private final BooleanProperty invalidSetupCode = new SimpleBooleanProperty(false);
|
||||
private final BooleanProperty workInProgress = new SimpleBooleanProperty(false);
|
||||
public TextField setupCodeField;
|
||||
public TextField deviceNameField;
|
||||
public Button registerBtn;
|
||||
|
||||
@Inject
|
||||
public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<JWEObject> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
|
||||
public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
|
||||
this.window = window;
|
||||
this.hubConfig = hubConfig;
|
||||
this.deviceId = deviceId;
|
||||
this.keyPair = Objects.requireNonNull(deviceKey.get());
|
||||
this.deviceKeyPair = Objects.requireNonNull(deviceKey.get());
|
||||
this.result = result;
|
||||
this.bearerToken = Objects.requireNonNull(bearerToken.get());
|
||||
this.registerSuccessScene = registerSuccessScene;
|
||||
this.registerFailedScene = registerFailedScene;
|
||||
this.jwt = JWT.decode(this.bearerToken);
|
||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
||||
}
|
||||
@@ -82,6 +86,13 @@ public class RegisterDeviceController implements FxController {
|
||||
public void initialize() {
|
||||
deviceNameField.setText(determineHostname());
|
||||
deviceNameField.textProperty().addListener(observable -> deviceNameAlreadyExists.set(false));
|
||||
deviceNameField.disableProperty().bind(workInProgress);
|
||||
setupCodeField.textProperty().addListener(observable -> invalidSetupCode.set(false));
|
||||
setupCodeField.disableProperty().bind(workInProgress);
|
||||
var missingSetupCode = setupCodeField.textProperty().isEmpty();
|
||||
var missingDeviceName = deviceNameField.textProperty().isEmpty();
|
||||
registerBtn.disableProperty().bind(workInProgress.or(missingSetupCode).or(missingDeviceName));
|
||||
registerBtn.contentDisplayProperty().bind(Bindings.when(workInProgress).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY));
|
||||
}
|
||||
|
||||
private String determineHostname() {
|
||||
@@ -95,35 +106,62 @@ public class RegisterDeviceController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void register() {
|
||||
deviceNameAlreadyExists.set(false);
|
||||
registerBtn.setContentDisplay(ContentDisplay.LEFT);
|
||||
registerBtn.setDisable(true);
|
||||
workInProgress.set(true);
|
||||
|
||||
var keyUri = URI.create(hubConfig.devicesResourceUrl + deviceId);
|
||||
var deviceKey = keyPair.getPublic().getEncoded();
|
||||
var dto = new CreateDeviceDto(deviceId, deviceNameField.getText(), BaseEncoding.base64Url().omitPadding().encode(deviceKey));
|
||||
var json = toJson(dto);
|
||||
var request = HttpRequest.newBuilder(keyUri) //
|
||||
var apiRootUrl = hubConfig.getApiBaseUrl();
|
||||
|
||||
var userReq = HttpRequest.newBuilder(apiRootUrl.resolve("users/me")) //
|
||||
.GET() //
|
||||
.timeout(REQ_TIMEOUT) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
.header("Content-Type", "application/json").PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
|
||||
.header("Content-Type", "application/json") //
|
||||
.build();
|
||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding()) //
|
||||
httpClient.sendAsync(userReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) //
|
||||
.thenApply(response -> {
|
||||
if (EXPECTED_RESPONSE_CODES.contains(response.statusCode())) {
|
||||
return response;
|
||||
if (response.statusCode() == 200) {
|
||||
var dto = fromJson(response.body());
|
||||
return Objects.requireNonNull(dto, "null or empty response body");
|
||||
} else {
|
||||
throw new RuntimeException("Server answered with unexpected status code " + response.statusCode());
|
||||
}
|
||||
}).handleAsync((response, throwable) -> {
|
||||
}).thenApply(user -> {
|
||||
try {
|
||||
assert user.privateKey != null; // api/vaults/{v}/user-tokens/me would have returned 403, if user wasn't fully set up yet
|
||||
var userKey = JWEHelper.decryptUserKey(JWEObject.parse(user.privateKey), setupCodeField.getText());
|
||||
return JWEHelper.encryptUserKey(userKey, deviceKeyPair.getPublic());
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException("Server answered with unparsable user key", e);
|
||||
}
|
||||
}).thenCompose(jwe -> {
|
||||
var now = Instant.now().toString();
|
||||
var dto = new CreateDeviceDto(deviceId, deviceNameField.getText(), BaseEncoding.base64().encode(deviceKeyPair.getPublic().getEncoded()), "DESKTOP", jwe.serialize(), now);
|
||||
var json = toJson(dto);
|
||||
var deviceUri = apiRootUrl.resolve("devices/" + deviceId);
|
||||
var putDeviceReq = HttpRequest.newBuilder(deviceUri) //
|
||||
.PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
|
||||
.timeout(REQ_TIMEOUT) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
.header("Content-Type", "application/json") //
|
||||
.build();
|
||||
return httpClient.sendAsync(putDeviceReq, HttpResponse.BodyHandlers.discarding());
|
||||
}).whenCompleteAsync((response, throwable) -> {
|
||||
if (response != null) {
|
||||
this.handleResponse(response);
|
||||
} else {
|
||||
this.registrationFailed(throwable);
|
||||
this.setupFailed(throwable);
|
||||
}
|
||||
return null;
|
||||
workInProgress.set(false);
|
||||
}, Platform::runLater);
|
||||
}
|
||||
|
||||
private UserDto fromJson(String json) {
|
||||
try {
|
||||
return JSON.reader().readValue(json, UserDto.class);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to deserialize DTO", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String toJson(CreateDeviceDto dto) {
|
||||
try {
|
||||
return JSON.writer().writeValueAsString(dto);
|
||||
@@ -132,23 +170,26 @@ public class RegisterDeviceController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleResponse(HttpResponse<Void> voidHttpResponse) {
|
||||
assert EXPECTED_RESPONSE_CODES.contains(voidHttpResponse.statusCode());
|
||||
|
||||
if (voidHttpResponse.statusCode() == 409) {
|
||||
deviceNameAlreadyExists.set(true);
|
||||
registerBtn.setContentDisplay(ContentDisplay.TEXT_ONLY);
|
||||
registerBtn.setDisable(false);
|
||||
} else {
|
||||
private void handleResponse(HttpResponse<Void> response) {
|
||||
if (response.statusCode() == 201) {
|
||||
LOG.debug("Device registration for hub instance {} successful.", hubConfig.authSuccessUrl);
|
||||
window.setScene(registerSuccessScene.get());
|
||||
} else if (response.statusCode() == 409) {
|
||||
deviceNameAlreadyExists.set(true);
|
||||
} else {
|
||||
setupFailed(new IllegalStateException("Unexpected http status code " + response.statusCode()));
|
||||
}
|
||||
}
|
||||
|
||||
private void registrationFailed(Throwable cause) {
|
||||
LOG.warn("Device registration failed.", cause);
|
||||
window.setScene(registerFailedScene.get());
|
||||
result.completeExceptionally(cause);
|
||||
private void setupFailed(Throwable cause) {
|
||||
switch (cause) {
|
||||
case CompletionException e when e.getCause() instanceof JWEHelper.InvalidJweKeyException -> invalidSetupCode.set(true);
|
||||
default -> {
|
||||
LOG.warn("Device setup failed.", cause);
|
||||
window.setScene(registerFailedScene.get());
|
||||
result.completeExceptionally(cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -160,13 +201,6 @@ public class RegisterDeviceController implements FxController {
|
||||
result.cancel(true);
|
||||
}
|
||||
|
||||
/* Getter */
|
||||
|
||||
public String getUserName() {
|
||||
return jwt.getClaim("email").asString();
|
||||
}
|
||||
|
||||
|
||||
//--- Getters & Setters
|
||||
|
||||
public BooleanProperty deviceNameAlreadyExistsProperty() {
|
||||
@@ -177,5 +211,21 @@ public class RegisterDeviceController implements FxController {
|
||||
return deviceNameAlreadyExists.get();
|
||||
}
|
||||
|
||||
public BooleanProperty invalidSetupCodeProperty() {
|
||||
return invalidSetupCode;
|
||||
}
|
||||
|
||||
public boolean isInvalidSetupCode() {
|
||||
return invalidSetupCode.get();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private record UserDto(String id, String name, String publicKey, String privateKey, String setupCode) {}
|
||||
|
||||
private record CreateDeviceDto(@JsonProperty(required = true) String id, //
|
||||
@JsonProperty(required = true) String name, //
|
||||
@JsonProperty(required = true) String publicKey, //
|
||||
@JsonProperty(required = true, defaultValue = "DESKTOP") String type, //
|
||||
@JsonProperty(required = true) String userPrivateKey, //
|
||||
@JsonProperty(required = true) String creationTime) {}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import java.util.concurrent.CompletableFuture;
|
||||
public class RegisterFailedController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final CompletableFuture<JWEObject> result;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
|
||||
@Inject
|
||||
public RegisterFailedController(@KeyLoading Stage window, CompletableFuture<JWEObject> result) {
|
||||
public RegisterFailedController(@KeyLoading Stage window, CompletableFuture<ReceivedKey> result) {
|
||||
this.window = window;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
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.application.Application;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@KeyLoadingScoped
|
||||
public class RequireAccountInitController implements FxController {
|
||||
|
||||
private final Application application;
|
||||
private final HubConfig hubConfig;
|
||||
private final Stage window;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
|
||||
@Inject
|
||||
public RequireAccountInitController(Application application, HubConfig hubConfig, @KeyLoading Stage window, CompletableFuture<ReceivedKey> result) {
|
||||
this.application = application;
|
||||
this.hubConfig = hubConfig;
|
||||
this.window = window;
|
||||
this.result = result;
|
||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void completeSetup() {
|
||||
application.getHostServices().showDocument(hubConfig.getWebappBaseUrl().resolve("profile").toString());
|
||||
close();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
window.close();
|
||||
}
|
||||
|
||||
private void windowClosed(WindowEvent windowEvent) {
|
||||
result.cancel(true);
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,10 @@ import java.util.concurrent.CompletableFuture;
|
||||
public class UnauthorizedDeviceController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final CompletableFuture<JWEObject> result;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
|
||||
@Inject
|
||||
public UnauthorizedDeviceController(@KeyLoading Stage window, CompletableFuture<JWEObject> result) {
|
||||
public UnauthorizedDeviceController(@KeyLoading Stage window, CompletableFuture<ReceivedKey> result) {
|
||||
this.window = window;
|
||||
this.result = result;
|
||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||
|
||||
@@ -26,6 +26,7 @@ public interface MainWindowComponent {
|
||||
default Stage showMainWindow() {
|
||||
Stage stage = window();
|
||||
stage.setScene(scene().get());
|
||||
stage.setIconified(false);
|
||||
stage.show();
|
||||
stage.toFront();
|
||||
stage.requestFocus();
|
||||
|
||||
@@ -16,6 +16,7 @@ import javafx.stage.Stage;
|
||||
public class MainWindowSceneFactory extends DefaultSceneFactory {
|
||||
|
||||
protected static final KeyCodeCombination SHORTCUT_N = new KeyCodeCombination(KeyCode.N, KeyCombination.SHORTCUT_DOWN);
|
||||
protected static final KeyCodeCombination SHORTCUT_O = new KeyCodeCombination(KeyCode.O, KeyCombination.SHORTCUT_DOWN);
|
||||
|
||||
private final Lazy<MainWindowTitleController> mainWindowTitleController;
|
||||
private final Lazy<VaultListController> vaultListController;
|
||||
@@ -34,6 +35,7 @@ public class MainWindowSceneFactory extends DefaultSceneFactory {
|
||||
} else {
|
||||
scene.getAccelerators().put(SHORTCUT_W, mainWindowTitleController.get()::close);
|
||||
}
|
||||
scene.getAccelerators().put(SHORTCUT_N, vaultListController.get()::didClickAddVault);
|
||||
scene.getAccelerators().put(SHORTCUT_N, vaultListController.get()::didClickAddNewVault);
|
||||
scene.getAccelerators().put(SHORTCUT_O, vaultListController.get()::didClickAddExistingVault);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,14 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Rectangle2D;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.stage.Screen;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
|
||||
@MainWindow
|
||||
public class ResizeController implements FxController {
|
||||
@@ -53,48 +52,57 @@ public class ResizeController implements FxController {
|
||||
public void initialize() {
|
||||
LOG.trace("init ResizeController");
|
||||
|
||||
if (neverTouched()) {
|
||||
settings.displayConfiguration.set(getMonitorSizes());
|
||||
return;
|
||||
} else {
|
||||
if (didDisplayConfigurationChange()) {
|
||||
//If the position is illegal, then the window appears on the main screen in the middle of the window.
|
||||
Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();
|
||||
window.setX((primaryScreenBounds.getWidth() - window.getMinWidth()) / 2);
|
||||
window.setY((primaryScreenBounds.getHeight() - window.getMinHeight()) / 2);
|
||||
window.setWidth(window.getMinWidth());
|
||||
window.setHeight(window.getMinHeight());
|
||||
} else {
|
||||
window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight());
|
||||
window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth());
|
||||
window.setX(settings.windowXPosition.get());
|
||||
window.setY(settings.windowYPosition.get());
|
||||
}
|
||||
if (!neverTouched()) {
|
||||
window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight());
|
||||
window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth());
|
||||
window.setX(settings.windowXPosition.get());
|
||||
window.setY(settings.windowYPosition.get());
|
||||
}
|
||||
savePositionalSettings();
|
||||
|
||||
window.setOnShowing(this::checkDisplayBounds);
|
||||
}
|
||||
|
||||
private boolean neverTouched() {
|
||||
return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0);
|
||||
}
|
||||
|
||||
private boolean didDisplayConfigurationChange() {
|
||||
String currentDisplayConfiguration = getMonitorSizes();
|
||||
String settingsDisplayConfiguration = settings.displayConfiguration.get();
|
||||
boolean configurationHasChanged = !settingsDisplayConfiguration.equals(currentDisplayConfiguration);
|
||||
if (configurationHasChanged) settings.displayConfiguration.set(currentDisplayConfiguration);
|
||||
return configurationHasChanged;
|
||||
private void checkDisplayBounds(WindowEvent evt) {
|
||||
// Minimizing a window in Windows and closing it could result in an out of bounds position at (x, y) = (-32000, -32000)
|
||||
// See https://devblogs.microsoft.com/oldnewthing/20041028-00/?p=37453
|
||||
// If the position is (-32000, -32000), restore to the last saved position
|
||||
if (window.getX() == -32000 && window.getY() == -32000) {
|
||||
window.setX(settings.windowXPosition.get());
|
||||
window.setY(settings.windowYPosition.get());
|
||||
window.setWidth(settings.windowWidth.get());
|
||||
window.setHeight(settings.windowHeight.get());
|
||||
}
|
||||
|
||||
if (isOutOfDisplayBounds()) {
|
||||
// If the position is illegal, then the window appears on the main screen in the middle of the window.
|
||||
LOG.debug("Resetting window position due to insufficient screen overlap");
|
||||
Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();
|
||||
window.setX((primaryScreenBounds.getWidth() - window.getMinWidth()) / 2);
|
||||
window.setY((primaryScreenBounds.getHeight() - window.getMinHeight()) / 2);
|
||||
window.setWidth(window.getMinWidth());
|
||||
window.setHeight(window.getMinHeight());
|
||||
savePositionalSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private String getMonitorSizes() {
|
||||
ObservableList<Screen> screens = Screen.getScreens();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < screens.size(); i++) {
|
||||
Rectangle2D screenBounds = screens.get(i).getBounds();
|
||||
if (!sb.isEmpty()) sb.append(" ");
|
||||
sb.append("displayId: " + i + ", " + screenBounds.getWidth() + "x" + screenBounds.getHeight() + ";");
|
||||
}
|
||||
return sb.toString();
|
||||
private boolean isOutOfDisplayBounds() {
|
||||
// define a rect which is inset on all sides from the window's rect:
|
||||
final double x = window.getX() + 20; // 20px left
|
||||
final double y = window.getY() + 5; // 5px top
|
||||
final double w = window.getWidth() - 40; // 20px left + 20px right
|
||||
final double h = window.getHeight() - 25; // 5px top + 20px bottom
|
||||
return isRectangleOutOfScreen(x, y, 0, h) // Left pixel column
|
||||
|| isRectangleOutOfScreen(x + w, y, 0, h) // Right pixel column
|
||||
|| isRectangleOutOfScreen(x, y, w, 0) // Top pixel row
|
||||
|| isRectangleOutOfScreen(x, y + h, w, 0); // Bottom pixel row
|
||||
}
|
||||
|
||||
private boolean isRectangleOutOfScreen(double x, double y, double width, double height) {
|
||||
return Screen.getScreensForRectangle(x, y, width, height).isEmpty();
|
||||
}
|
||||
|
||||
private void startResize(MouseEvent evt) {
|
||||
@@ -183,5 +191,4 @@ public class ResizeController implements FxController {
|
||||
public boolean isShowResizingArrows() {
|
||||
return showResizingArrows.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||
import org.cryptomator.cryptofs.DirStructure;
|
||||
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -21,6 +22,9 @@ import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Side;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.input.ContextMenuEvent;
|
||||
import javafx.scene.input.DragEvent;
|
||||
@@ -34,6 +38,8 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -59,12 +65,25 @@ public class VaultListController implements FxController {
|
||||
private final RemoveVaultComponent.Builder removeVaultDialogue;
|
||||
private final VaultListManager vaultListManager;
|
||||
private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty();
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final FxApplicationWindows appWindows;
|
||||
|
||||
public ListView<Vault> vaultList;
|
||||
public StackPane root;
|
||||
public Button addVaultBtn;
|
||||
@FXML
|
||||
private ContextMenu addVaultContextMenu;
|
||||
|
||||
@Inject
|
||||
VaultListController(@MainWindow Stage mainWindow, ObservableList<Vault> vaults, ObjectProperty<Vault> selectedVault, VaultListCellFactory cellFactory, AddVaultWizardComponent.Builder addVaultWizard, RemoveVaultComponent.Builder removeVaultDialogue, VaultListManager vaultListManager) {
|
||||
VaultListController(@MainWindow Stage mainWindow, //
|
||||
ObservableList<Vault> vaults, //
|
||||
ObjectProperty<Vault> selectedVault, //
|
||||
VaultListCellFactory cellFactory, //
|
||||
AddVaultWizardComponent.Builder addVaultWizard, //
|
||||
RemoveVaultComponent.Builder removeVaultDialogue, //
|
||||
VaultListManager vaultListManager, //
|
||||
ResourceBundle resourceBundle, //
|
||||
FxApplicationWindows appWindows) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.vaults = vaults;
|
||||
this.selectedVault = selectedVault;
|
||||
@@ -72,6 +91,8 @@ public class VaultListController implements FxController {
|
||||
this.addVaultWizard = addVaultWizard;
|
||||
this.removeVaultDialogue = removeVaultDialogue;
|
||||
this.vaultListManager = vaultListManager;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.appWindows = appWindows;
|
||||
|
||||
this.emptyVaultList = Bindings.isEmpty(vaults);
|
||||
|
||||
@@ -92,6 +113,15 @@ public class VaultListController implements FxController {
|
||||
});
|
||||
vaultList.addEventFilter(MouseEvent.MOUSE_RELEASED, this::deselect);
|
||||
|
||||
//unlock vault on double click
|
||||
vaultList.addEventFilter(MouseEvent.MOUSE_CLICKED, click -> {
|
||||
if (click.getClickCount() >= 2) {
|
||||
Optional.ofNullable(selectedVault.get())
|
||||
.filter(Vault::isLocked)
|
||||
.ifPresent(vault -> appWindows.startUnlockWorkflow(vault, mainWindow));
|
||||
}
|
||||
});
|
||||
|
||||
//don't show context menu when no vault selected
|
||||
vaultList.addEventFilter(ContextMenuEvent.CONTEXT_MENU_REQUESTED, request -> {
|
||||
if (selectedVault.get() == null) {
|
||||
@@ -129,6 +159,15 @@ public class VaultListController implements FxController {
|
||||
root.setOnDragExited(this::handleDragEvent);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void toggleMenu() {
|
||||
if (addVaultContextMenu.isShowing()) {
|
||||
addVaultContextMenu.hide();
|
||||
} else {
|
||||
addVaultContextMenu.show(addVaultBtn, Side.BOTTOM, 0.0, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
private void deselect(MouseEvent released) {
|
||||
if (released.getY() > (vaultList.getItems().size() * vaultList.fixedCellSizeProperty().get())) {
|
||||
vaultList.getSelectionModel().clearSelection();
|
||||
@@ -144,8 +183,13 @@ public class VaultListController implements FxController {
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickAddVault() {
|
||||
addVaultWizard.build().showAddVaultWizard();
|
||||
public void didClickAddNewVault() {
|
||||
addVaultWizard.build().showAddNewVaultWizard(resourceBundle);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickAddExistingVault() {
|
||||
addVaultWizard.build().showAddExistingVaultWizard(resourceBundle);
|
||||
}
|
||||
|
||||
private void pressedShortcutToRemoveVault() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.ui.preferences;
|
||||
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.UpdateChecker;
|
||||
import org.slf4j.Logger;
|
||||
@@ -19,6 +20,7 @@ public class PreferencesController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PreferencesController.class);
|
||||
|
||||
private final Environment env;
|
||||
private final Stage window;
|
||||
private final ObjectProperty<SelectedPreferencesTab> selectedTabProperty;
|
||||
private final BooleanBinding updateAvailable;
|
||||
@@ -31,7 +33,8 @@ public class PreferencesController implements FxController {
|
||||
public Tab aboutTab;
|
||||
|
||||
@Inject
|
||||
public PreferencesController(@PreferencesWindow Stage window, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, UpdateChecker updateChecker) {
|
||||
public PreferencesController(Environment env, @PreferencesWindow Stage window, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, UpdateChecker updateChecker) {
|
||||
this.env = env;
|
||||
this.window = window;
|
||||
this.selectedTabProperty = selectedTabProperty;
|
||||
this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
|
||||
@@ -42,6 +45,9 @@ public class PreferencesController implements FxController {
|
||||
window.setOnShowing(this::windowWillAppear);
|
||||
selectedTabProperty.addListener(observable -> this.selectChosenTab());
|
||||
tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged());
|
||||
if (env.disableUpdateCheck()) {
|
||||
tabPane.getTabs().remove(updatesTab);
|
||||
}
|
||||
}
|
||||
|
||||
private void selectChosenTab() {
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -58,7 +59,7 @@ public class RecoveryKeyFactory {
|
||||
}
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
@VisibleForTesting
|
||||
String createRecoveryKey(byte[] rawKey) {
|
||||
Preconditions.checkArgument(rawKey.length == 64, "key should be 64 bytes");
|
||||
byte[] paddedKey = Arrays.copyOf(rawKey, 66);
|
||||
|
||||
@@ -795,6 +795,16 @@
|
||||
-fx-scale-shape: false;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Add Vault - MenuItem *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.add-vault-menu-item {
|
||||
-fx-padding: 4px 8px;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* ProgressBar *
|
||||
|
||||
@@ -794,6 +794,16 @@
|
||||
-fx-scale-shape: false;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Add Vault - MenuItem *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.add-vault-menu-item {
|
||||
-fx-padding: 4px 8px;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* ProgressBar *
|
||||
|
||||
@@ -24,9 +24,8 @@
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="B+X">
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+X">
|
||||
<buttons>
|
||||
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" onAction="#back"/>
|
||||
<Button fx:id="finishButton" text="%addvaultwizard.existing.chooseBtn" ButtonBar.buttonData="NEXT_FORWARD" onAction="#chooseFileAndNext" defaultButton="true"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
|
||||
@@ -68,9 +68,8 @@
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="B+X">
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+X">
|
||||
<buttons>
|
||||
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" onAction="#back"/>
|
||||
<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" onAction="#next" defaultButton="true" disable="${!controller.validVaultName}"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?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.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.addvaultwizard.AddVaultWelcomeController"
|
||||
prefWidth="450"
|
||||
prefHeight="450"
|
||||
spacing="12"
|
||||
alignment="TOP_CENTER">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="24"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<ImageView VBox.vgrow="ALWAYS" fitHeight="128" preserveRatio="true" smooth="true" cache="true">
|
||||
<Image url="@../img/logo.png"/>
|
||||
</ImageView>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<VBox alignment="CENTER" spacing="9">
|
||||
<Button styleClass="button-large" text="%addvaultwizard.welcome.newButton" onAction="#createNewVault" prefWidth="Infinity">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="MAGIC" glyphSize="15"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button styleClass="button-large" text="%addvaultwizard.welcome.existingButton" onAction="#chooseExistingVault" prefWidth="Infinity">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="FOLDER_OPEN" glyphSize="15"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
</VBox>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
</children>
|
||||
</VBox>
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.keyloading.hub.RegisterDeviceController"
|
||||
fx:controller="org.cryptomator.ui.keyloading.hub.LegacyRegisterDeviceController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
@@ -0,0 +1,60 @@
|
||||
<?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?>
|
||||
<?import javafx.scene.text.Text?>
|
||||
<?import javafx.scene.control.Hyperlink?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.keyloading.hub.RequireAccountInitController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12"
|
||||
alignment="TOP_LEFT">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="USER_COG" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="label-large" text="%hub.requireAccountInit.message" wrapText="true" textAlignment="LEFT">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
|
||||
<TextFlow styleClass="text-flow">
|
||||
<Text text="%hub.requireAccountInit.description.0"/>
|
||||
<Text text=" "/>
|
||||
<Hyperlink styleClass="hyperlink-underline" text="%hub.requireAccountInit.description.1" onAction="#completeSetup"/>
|
||||
<Text text="%hub.requireAccountInit.description.2"/>
|
||||
</TextFlow>
|
||||
|
||||
<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>
|
||||
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.Group?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.keyloading.hub.RegisterDeviceController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12"
|
||||
alignment="TOP_LEFT">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="INFO" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="label-large" text="%hub.register.message" wrapText="true" textAlignment="LEFT">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="%hub.register.description" wrapText="true"/>
|
||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
||||
<padding>
|
||||
<Insets top="12"/>
|
||||
</padding>
|
||||
<Label text="Account Key" labelFor="$setupCodeField"/>
|
||||
<TextField fx:id="setupCodeField" HBox.hgrow="ALWAYS"/>
|
||||
</HBox>
|
||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
||||
<padding>
|
||||
<Insets top="12"/>
|
||||
</padding>
|
||||
<Label text="%hub.register.nameLabel" labelFor="$deviceNameField"/>
|
||||
<TextField fx:id="deviceNameField" HBox.hgrow="ALWAYS"/>
|
||||
</HBox>
|
||||
<HBox alignment="TOP_RIGHT">
|
||||
<Label text="%hub.register.occupiedMsg" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.deviceNameAlreadyExists}" managed="${controller.deviceNameAlreadyExists}" graphicTextGap="6">
|
||||
<padding>
|
||||
<Insets top="6"/>
|
||||
</padding>
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
|
||||
</graphic>
|
||||
</Label>
|
||||
|
||||
<Label text="%hub.register.invalidAccountKeyLabel" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.invalidSetupCode}" managed="${controller.invalidSetupCode}" graphicTextGap="6">
|
||||
<padding>
|
||||
<Insets top="6"/>
|
||||
</padding>
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
|
||||
</graphic>
|
||||
</Label>
|
||||
</HBox>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CU">
|
||||
<buttons>
|
||||
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
|
||||
<Button fx:id="registerBtn" text="%hub.register.registerBtn" ButtonBar.buttonData="OTHER" defaultButton="true" onAction="#register" contentDisplay="TEXT_ONLY" >
|
||||
<graphic>
|
||||
<FontAwesome5Spinner glyphSize="12" />
|
||||
</graphic>
|
||||
</Button>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</HBox>
|
||||
@@ -8,6 +8,8 @@
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Arc?>
|
||||
<?import javafx.scene.control.ContextMenu?>
|
||||
<?import javafx.scene.control.MenuItem?>
|
||||
<StackPane xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:id="root"
|
||||
@@ -26,11 +28,27 @@
|
||||
<Arc VBox.vgrow="NEVER" styleClass="onboarding-overlay-arc" type="OPEN" centerX="50" centerY="0" radiusY="100" radiusX="50" startAngle="0" length="-60" strokeWidth="1"/>
|
||||
</VBox>
|
||||
</StackPane>
|
||||
<Button styleClass="toolbar-button" text="%main.vaultlist.addVaultBtn" onAction="#didClickAddVault" alignment="BASELINE_CENTER" maxWidth="Infinity">
|
||||
<Button fx:id="addVaultBtn" onAction="#toggleMenu" styleClass="toolbar-button" text="%main.vaultlist.addVaultBtn" alignment="BASELINE_CENTER" maxWidth="Infinity" contentDisplay="RIGHT">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="PLUS"/>
|
||||
<FontAwesome5IconView glyph="CARET_DOWN"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
<fx:define>
|
||||
<ContextMenu fx:id="addVaultContextMenu">
|
||||
<items>
|
||||
<MenuItem styleClass="add-vault-menu-item" text="%main.vaultlist.addVaultBtn.menuItemNew" onAction="#didClickAddNewVault" >
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="PLUS" textAlignment="CENTER" wrappingWidth="14" />
|
||||
</graphic>
|
||||
</MenuItem>
|
||||
<MenuItem styleClass="add-vault-menu-item" text="%main.vaultlist.addVaultBtn.menuItemExisting" onAction="#didClickAddExistingVault" >
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="FOLDER_OPEN" textAlignment="CENTER" wrappingWidth="14" />
|
||||
</graphic>
|
||||
</MenuItem>
|
||||
</items>
|
||||
</ContextMenu>
|
||||
</fx:define>
|
||||
</VBox>
|
||||
<Region styleClass="drag-n-drop-border" visible="${controller.draggingVaultOver}"/>
|
||||
</StackPane>
|
||||
|
||||
@@ -41,10 +41,8 @@ traymenu.vault.reveal=Reveal
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Add Vault
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Create New Vault
|
||||
addvaultwizard.welcome.existingButton=Open Existing Vault
|
||||
## New
|
||||
addvaultwizard.new.title=Add New Vault
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Choose a name for the vault
|
||||
addvaultwizard.new.namePrompt=Vault Name
|
||||
@@ -94,6 +92,7 @@ addvault.new.readme.accessLocation.2=This is your vault's access location.
|
||||
addvault.new.readme.accessLocation.3=Any files added to this volume will be encrypted by Cryptomator. You can work on it like on any other drive/folder. This is only a decrypted view of its content, your files stay encrypted on your hard drive all the time.
|
||||
addvault.new.readme.accessLocation.4=Feel free to remove this file.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Add Existing Vault
|
||||
addvaultwizard.existing.instruction=Choose the "vault.cryptomator" file of your existing vault. If only a file named "masterkey.cryptomator" exists, select that instead.
|
||||
addvaultwizard.existing.chooseBtn=Choose…
|
||||
addvaultwizard.existing.filePickerTitle=Select Vault File
|
||||
@@ -139,7 +138,7 @@ unlock.error.customPath.message=Unable to mount vault to custom path
|
||||
unlock.error.customPath.description.notSupported=If you wish to keep using the custom path, please go to the preferences and select a volume type that supports it. Otherwise, go to the vault options and choose a supported mount point.
|
||||
unlock.error.customPath.description.notExists=The custom mount path does not exist. Either create it in your local filesystem or change it in the vault options.
|
||||
unlock.error.customPath.description.inUse=The drive letter or custom mount path "%s" is already in use.
|
||||
unlock.error.customPath.description.hideawayNotDir=The temporary, hidden file "%3$s" used for unlock could not be removed. Please check the file and then delete it manually.
|
||||
unlock.error.customPath.description.hideawayNotDir=The temporary, hidden file "%3$s" used for unlocking could not be removed. Please check the file and then delete it manually.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Your vault could not be mounted to the path "%s". Please try again or choose a different path.
|
||||
unlock.error.customPath.description.notEmptyDir=The custom mount path "%s" is not an empty folder. Please choose an empty folder and try again.
|
||||
unlock.error.customPath.description.generic=You have selected a custom mount path for this vault, but using it failed with the message: %2$s
|
||||
@@ -155,9 +154,10 @@ hub.auth.loginLink=Not redirected? Click here to open it.
|
||||
hub.receive.message=Processing response…
|
||||
hub.receive.description=Cryptomator is receiving and processing the response from Hub. Please wait.
|
||||
### Register Device
|
||||
hub.register.message=Device name required
|
||||
hub.register.description=This seems to be the first Hub access from this device. In order to identify it for access authorization, you need to name this device.
|
||||
hub.register.message=New Device
|
||||
hub.register.description=This is the first Hub access from this device. Please authorize it using your Account Key.
|
||||
hub.register.nameLabel=Device Name
|
||||
hub.register.invalidAccountKeyLabel=Invalid Account Key
|
||||
hub.register.occupiedMsg=Name already in use
|
||||
hub.register.registerBtn=Confirm
|
||||
### Registration Success
|
||||
@@ -169,6 +169,11 @@ hub.registerFailed.description=An error was thrown in the naming process. For mo
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Access denied
|
||||
hub.unauthorized.description=Your device has not yet been authorized to access this vault. Ask the vault owner to authorize it.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Action required
|
||||
hub.requireAccountInit.description.0=To proceed, please complete the steps required in your
|
||||
hub.requireAccountInit.description.1=Hub user profile
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Hub License invalid
|
||||
hub.invalidLicense.description=Your Cryptomator Hub instance has an invalid license. Please inform a Hub administrator to upgrade or renew the license.
|
||||
@@ -367,7 +372,9 @@ main.vaultlist.contextMenu.unlock=Unlock…
|
||||
main.vaultlist.contextMenu.unlockNow=Unlock Now
|
||||
main.vaultlist.contextMenu.vaultoptions=Show Vault Options
|
||||
main.vaultlist.contextMenu.reveal=Reveal Drive
|
||||
main.vaultlist.addVaultBtn=Add Vault
|
||||
main.vaultlist.addVaultBtn=Add
|
||||
main.vaultlist.addVaultBtn.menuItemNew=New Vault...
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Existing Vault...
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Thanks for choosing Cryptomator to protect your files. If you need any assistance, check out our getting started guides:
|
||||
|
||||
@@ -40,10 +40,8 @@ traymenu.vault.reveal=إظهار
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=أضِف مخزنًا
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=إنشاء مخزن جديد
|
||||
addvaultwizard.welcome.existingButton=افتح مخزن موجود
|
||||
## New
|
||||
addvaultwizard.new.title=إضافة مخزن جديد
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=اختر اسم للمخزن
|
||||
addvaultwizard.new.namePrompt=اسم الخزينة
|
||||
@@ -88,11 +86,12 @@ addvault.new.readme.storageLocation.8=2. فتح المخزن باستخدام Cr
|
||||
addvault.new.readme.storageLocation.9=3. افتح موقع الوصول بالنقر على زر "إظهار".
|
||||
addvault.new.readme.storageLocation.10=إذا كنت بحاجة إلى المساعدة، قم بالاطلاع على كتيب الوثائق: %s
|
||||
addvault.new.readme.accessLocation.fileName=مرحبا.rtf
|
||||
addvault.new.readme.accessLocation.1=🔐️ المجلدات المشفرة 🔐️
|
||||
addvault.new.readme.accessLocation.1=🔐️ وحدة التخزين مشفرة 🔐️
|
||||
addvault.new.readme.accessLocation.2=هذا هو موقع الوصول للخرنة الخاص بك.
|
||||
addvault.new.readme.accessLocation.3=سيتم تشفير أي ملفات تضاف إلى هذا المجلد من قبل Cryptomator. يمكنك العمل عليه كأي مجلد آخر. هذه فقط طريقة عرض غير مشفرة لمحتوياتها، ملفاتك تبقى مشفرة على القرص الصلب الخاص بك طوال الوقت.
|
||||
addvault.new.readme.accessLocation.4=لا تتردد في إزالة هذا الملف.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=أضِف مخزن موجود
|
||||
addvaultwizard.existing.instruction=اختر ملف "vault.cryptomator" من خزينتك الحالية. إذا كان يوجد فقط ملف اسمه "masterkey.cryptomator"، اختره عوضاً عنه.
|
||||
addvaultwizard.existing.chooseBtn=اختر…
|
||||
addvaultwizard.existing.filePickerTitle=حدد مِلَفّ المخزن
|
||||
@@ -135,10 +134,10 @@ unlock.success.rememberChoice=تذكر اختياري ولا تظهر هذا م
|
||||
unlock.success.revealBtn=اظهار القرص
|
||||
## Failure
|
||||
unlock.error.customPath.message=غير قادر على تركيب المخزن إلى المسار المخصص
|
||||
unlock.error.customPath.description.notSupported=إذا كنت ترغب في الاستمرار في استخدام المسار المخصص، يرجى الذَّهاب إلى التفضيلات وتحديد نوع واسطة التخزين الذي يدعمها. خلاف ذلك، انتقل إلى خيارات المخزن واختر نقطة تركيب مدعومة.
|
||||
unlock.error.customPath.description.notSupported=إذا كنت ترغب في الاستمرار في استخدام المسار المخصص، يرجى الذَّهاب إلى التفضيلات وتحديد نوع وحدة التخزين\n الذي يدعمها. خلاف ذلك، انتقل إلى خيارات المخزن واختر نقطة تركيب مدعومة.
|
||||
unlock.error.customPath.description.notExists=مسار التركيب المخصص غير موجود. قم بإنشائه في نظام الملفات المحلي الخاص بك أو اختر غيره في خيارات الخزانة.
|
||||
unlock.error.customPath.description.inUse=حرف القرص أو مسار التركيب المخصص "%s" قيد الاستخدام بالفعل.
|
||||
unlock.error.customPath.description.hideawayNotDir=لا يمكن إزالة الملف المؤقت المخفي "%3$s" المستخدم للفتح. الرجاء التحقق من الملف ثم حذفه يدوياً.
|
||||
unlock.error.customPath.description.hideawayNotDir=لا يمكن إزالة المِلَفّ المؤقت المخفي "%3$s" المستخدم لفتح القُفْل. الرجاء التحقق من المِلَفّ ثم حذفه يدوياً.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=لا يمكن تركيب خزانتك على المسار "%s". الرجاء المحاولة مرة أخرى أو اختيار مسار مختلف.
|
||||
unlock.error.customPath.description.notEmptyDir=مسار التركيب المخصص "%s" ليس مجلد فارغ. الرجاء اختيار مجلد فارغ وحاول مرة أخرى.
|
||||
unlock.error.customPath.description.generic=لقد اخترت مسار تركيب مخصص لهذه الخزانة، ولكن استخدامه فشل مع الرسالة: %2$s
|
||||
@@ -154,8 +153,6 @@ hub.auth.loginLink=لم يتم إعادة توجيهك؟ انقر هنا لفت
|
||||
hub.receive.message=معالجة الاستجابة…
|
||||
hub.receive.description=Cryptomator يتلقى ويعالج الاستجابة من المركز. الرجاء الانتظار.
|
||||
### Register Device
|
||||
hub.register.message=اسم الجهاز متطلب
|
||||
hub.register.description=يبدو أن هذا هو أول وصول للمركز من هذا الجهاز. من أجل تحديده للحصول على إذن الوصول، تحتاج إلى تسمية هذا الجهاز.
|
||||
hub.register.nameLabel=اسم الجهاز
|
||||
hub.register.occupiedMsg=الاسم مستخدم مسبقاً
|
||||
hub.register.registerBtn=تأكيد
|
||||
@@ -168,6 +165,7 @@ hub.registerFailed.description=خطأ في عملية التسمية. لمزيد
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=تم رفض الوصول
|
||||
hub.unauthorized.description=لم يتم بعد منح الإذن لجهازك بالوصول إلى هذا المخزن. اطلب من مالك المخزن أن يأذن بذلك.
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=ترخيص المركز غير صالح
|
||||
hub.invalidLicense.description=نموذج المركز Cryptomator الخاص بك لديه ترخيص غير صالح. الرجاء إبلاغ مسؤول مركز لترقية أو تجديد الترخيص.
|
||||
@@ -249,22 +247,52 @@ health.result.severityFilter.info=معلومات
|
||||
health.result.severityFilter.warn=تحذير
|
||||
health.result.severityFilter.crit=حرج
|
||||
health.result.severityTip.good=الخطورة: جيد\nبنية المخزن عادية.
|
||||
health.result.severityTip.info=الخطورة: معلومات\nبنية المخزن سليمة، إصلاح المقترح.
|
||||
health.result.severityTip.warn=الخطورة: تحذير\nبنية المخزن تالفة، ننصح لإصلاح.
|
||||
health.result.severityTip.crit=الخطورة: حرجة\nبنية المخزن تالفة، وتم تحديد فقدان البيانات.
|
||||
health.result.fixStateFilter.all=حالة لإصلاح - الكل
|
||||
health.result.fixStateFilter.fixable=قابلة للإصلاح
|
||||
health.result.fixStateFilter.notFixable=غير قابل للإصلاح
|
||||
health.result.fixStateFilter.fixing=يتم الإصلاح…
|
||||
health.result.fixStateFilter.fixed=تم الإصلاح
|
||||
health.result.fixStateFilter.fixFailed=فشل الإصلاح
|
||||
## Fix Application
|
||||
health.fix.fixBtn=إصلاح
|
||||
health.fix.successTip=تم الإصلاح بنجاح
|
||||
health.fix.failTip=فشل الإصلاح، راجع السجل للحصول على التفاصيل
|
||||
|
||||
# Preferences
|
||||
preferences.title=تفضيلات
|
||||
## General
|
||||
preferences.general=عام
|
||||
preferences.general.startHidden=إخفاء النافذة عند بدء تشغيل Cryptomator
|
||||
preferences.general.autoCloseVaults=اقفل الخزانات المفتوحة تلقائياً عند الإقلاع عن التطبيق
|
||||
preferences.general.debugLogging=تمكين سجلات التصحيح
|
||||
preferences.general.debugDirectory=عرض ملفات السجل
|
||||
preferences.general.autoStart=تشغيل Cryptomator عند بدء تشغيل النظام
|
||||
preferences.general.keychainBackend=تخزين كلمات المرور مع
|
||||
## Interface
|
||||
preferences.interface=الواجهة
|
||||
preferences.interface.theme=الشكل والمظهر
|
||||
preferences.interface.theme.automatic=تلقائي
|
||||
preferences.interface.theme.dark=مظلم (أسود)
|
||||
preferences.interface.theme.light=فاتح (أبيض)
|
||||
preferences.interface.unlockThemes=تفعيل الوضع الداكن
|
||||
preferences.interface.language=اللغة (يتطلب إعادة التشغيل)
|
||||
preferences.interface.language.auto=النظام الافتراضي
|
||||
preferences.interface.interfaceOrientation=اتجاه الواجهة
|
||||
preferences.interface.interfaceOrientation.ltr=من اليسار إلى اليمين
|
||||
preferences.interface.interfaceOrientation.rtl=من اليمين إلى اليسار
|
||||
preferences.interface.showMinimizeButton=إظهار زر التصغير
|
||||
preferences.interface.showTrayIcon=إظهار أيقونة اللوحة (يتطلب إعادة تشغيل)
|
||||
## Volume
|
||||
preferences.volume=القرص الإفتراضي
|
||||
preferences.volume.type=نوع وحدة التخزين
|
||||
preferences.volume.type.automatic=تلقائي
|
||||
preferences.volume.docsTooltip=افتح الوثائق لمعرفة المزيد عن مختلف أنواع وحدة التخزين.
|
||||
preferences.volume.fuseRestartRequired=لتطبيق التغييرات، يحتاج Cryptomator إلى إعادة التشغيل.
|
||||
preferences.volume.tcp.port=منفذ TCP
|
||||
preferences.volume.supportedFeatures=يدعم نوع وحدة تخزين المختار الميزات التالية:
|
||||
## Updates
|
||||
preferences.updates=تحديثات
|
||||
preferences.updates.currentVersion=الإصدار الحالي: %s
|
||||
@@ -310,7 +338,7 @@ main.vaultlist.contextMenu.unlock=فتح…
|
||||
main.vaultlist.contextMenu.unlockNow=افتح الان
|
||||
main.vaultlist.contextMenu.vaultoptions=إظهار خيارات المخزن
|
||||
main.vaultlist.contextMenu.reveal=اظهار القرص
|
||||
main.vaultlist.addVaultBtn=أضِف مخزنًا
|
||||
main.vaultlist.addVaultBtn=إضافة
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=شكرا لاختيار Cryptomator لحماية ملفاتك. إذا كنت بحاجة إلى أية مساعدة، تحقق من دليل وتعليمات الإستخدام:
|
||||
@@ -350,8 +378,8 @@ wrongFileAlert.message=هل حاولت تشفير هذه الملفات؟
|
||||
wrongFileAlert.description=لهذا الغرض، بوفر Cryptomator مجلداً في مدير ملفات النظام الخاص بك.
|
||||
wrongFileAlert.instruction.0=لتشفير الملفات، قم باتباع الخطوات التالية:
|
||||
wrongFileAlert.instruction.1=1. فتح المخزن الخاص بك.
|
||||
wrongFileAlert.instruction.2=2. انقر على "إظهار" لفتح المجلد في مدير الملفات الخاص بك.
|
||||
wrongFileAlert.instruction.3=3. أضف ملفاتك إلى هذا المجلد.
|
||||
wrongFileAlert.instruction.2=2. انقر على "إظهار" لفتح وحدة التخزين في مدير ملفاتك.
|
||||
wrongFileAlert.instruction.3=3. أضِف ملفاتك إلى هذه وحدة التخزين.
|
||||
wrongFileAlert.link=لمزيد من المساعدة، قم بزيارة
|
||||
|
||||
# Vault Options
|
||||
|
||||
@@ -22,6 +22,9 @@ error.hyperlink.report=Паведаміць пра гэтую памылку
|
||||
error.technicalDetails=Падрабязнасці:
|
||||
error.existingSolutionDescription=Cryptomator не чакаў такога, але мы знайшлі, як можна выправіць гэтую хібу. Калі ласка, скарыстайся спасылкаю.
|
||||
error.hyperlink.solution=Паглядзець на рашэнне
|
||||
error.lookupPermissionMessage=Cryptomator можа шукаць рашэнне праблемаў онлайн, дасылаючы запыт да нашай база даных хібаў з твайго IP-адрасу.
|
||||
error.dismiss=Адхіліць
|
||||
error.lookUpSolution=Пашукаць вырашэнне
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=Скарбніца
|
||||
@@ -37,10 +40,8 @@ traymenu.vault.reveal=Паказаць
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Дадаць скарбніцу
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Стварыць новую скарбніцу
|
||||
addvaultwizard.welcome.existingButton=Адчыніць існуючую скарбніцу
|
||||
## New
|
||||
addvaultwizard.new.title=Дадаць новую скарбніцу
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Абраць назву для скарбніцы
|
||||
addvaultwizard.new.namePrompt=Назва скарбніцы
|
||||
@@ -62,6 +63,11 @@ addvaultwizard.new.validCharacters.chars=Слоўныя знакі, накшта
|
||||
addvaultwizard.new.validCharacters.numbers=Лічбы
|
||||
addvaultwizard.new.validCharacters.dashes=Злучок (%s) або падкрэслінік (%s)
|
||||
### Expert Settings
|
||||
addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=Актываваць экспэртныя функцыі
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.invalid=Увядзі значэнне ад 36 да 220 (прадвызначана 220)
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=Адкрый дакумэнтацыю, каб атрымаць больш інфармацыі.
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.title=Максымальная даўжыня назваў зашыфраваных файлаў
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.valid=Дзейсны
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Стварыць скарбніцу
|
||||
addvaultwizard.new.generateRecoveryKeyChoice=Бяз гэтага пароля ты ня зможаш атрымаць доступ да сваіх даных. Ці хочаш ты мець ключ аднаўлення на выпадак, калі ты згубіш свой пароль?
|
||||
@@ -85,6 +91,7 @@ addvault.new.readme.accessLocation.2=Гэта месца дуступу да т
|
||||
addvault.new.readme.accessLocation.3=Любы дададзены сюды файл будзе зашыфраваны праз Cryptomator. Ты можаш працаваць тут як са звычайнаю тэчкаю альбо дыскам. Гэта толькі расшыфраваны агляд кантэнту, самі файлы заўжды захоўваюцца зашыфраванымі на тваім цвёрдым дыску.
|
||||
addvault.new.readme.accessLocation.4=Ты можаш выдаліць гэты файл.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Дадаць існуючую скрабніцу
|
||||
addvaultwizard.existing.instruction=Абяры файл "vault.cryptomator" у існуючай скарбніцы. Калі існуе толькі файл "masterkey.cryptomator", абяры яго.
|
||||
addvaultwizard.existing.chooseBtn=Абраць…
|
||||
addvaultwizard.existing.filePickerTitle=Абяры файл скарбніцы
|
||||
@@ -129,6 +136,7 @@ unlock.success.revealBtn=Паказаць дыск
|
||||
unlock.error.customPath.message=Не магчыма змантажаваць скарбніцу да карыстальніцкай сцежкі
|
||||
unlock.error.customPath.description.notSupported=Калі ты надалей жадаеш карыстацца адмысловаю сцежкаю, калі ласка, пайдзі ў налады ды абары там тып тому, які падтрымлівае яе. У іншым выпадку пайдзі ў опцыі скарбніцы ды абяры там пункт мантажавання, які падтрымліваецца.
|
||||
unlock.error.customPath.description.notExists=Адмысловая сцежка мантажавання не існуе. Ствары яе ў сваёй файлавай сістэме, альбо змяні яе ў опцыях скарбніцы.
|
||||
unlock.error.customPath.description.inUse=Дыскавая літара або карыстальніцкая мантажная сьцежка "%s" ужо выкарыстоўваецца.
|
||||
## Hub
|
||||
hub.noKeychain.message=Няма доступу да ключа прылады
|
||||
hub.noKeychain.description=Каб разамкнуць скарбніцы Hub, патрэбны ключ прылады, які захаваны ў звязку ключоў. Каб працягнуць, уключы "%s" ды абяры звязак ключоў у наладах.
|
||||
@@ -141,8 +149,7 @@ hub.auth.loginLink=Не перанакіраваўся? Пстрыкні сюд
|
||||
hub.receive.message=Апрацоўка адказу…
|
||||
hub.receive.description=Cryptomator атрымлівае ды апрацоўвае адказ ад Hub. Калі ласка, пачакай.
|
||||
### Register Device
|
||||
hub.register.message=Патрабуецца назва прылады
|
||||
hub.register.description=Здаецца, што ты ў першы раз увайшла/-оў у Hub з гэтай прылады. Каб ідэнтыфікаваць яе для спраўджання доступу, табе трэба назваць гэтую прыладу.
|
||||
hub.register.message=Новая прылада
|
||||
hub.register.nameLabel=Назва прылады
|
||||
hub.register.occupiedMsg=Назва ўжо ўжытая
|
||||
hub.register.registerBtn=Пацвердзіць
|
||||
@@ -155,6 +162,7 @@ hub.registerFailed.description=Падчас прысваення імя адбы
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Адмова ў доступе
|
||||
hub.unauthorized.description=Тваёй прыладзе ў дадзены момант не дазволена мець доступ да гэтай скрабніцы. Запытайся ўладальніка скрабніцы за дазволам.
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Несапраўдная ліцэнзія Hub
|
||||
hub.invalidLicense.description=Твая інстанцыя Cryptomator Hub мае некарэктную ліцэнзію. Калі ласка, паведамі адміністратару Hub пра гэта, каб абнавіць альбо аднавіць ліцэнзію.
|
||||
@@ -353,7 +361,9 @@ main.vaultlist.contextMenu.unlock=Адамкнуць…
|
||||
main.vaultlist.contextMenu.unlockNow=Разамкнуць зараз
|
||||
main.vaultlist.contextMenu.vaultoptions=Паказаць параметры скарбніцы
|
||||
main.vaultlist.contextMenu.reveal=Паказаць дыск
|
||||
main.vaultlist.addVaultBtn=Дадаць скарбніцу
|
||||
main.vaultlist.addVaultBtn=Дадаць
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Новая скарбніца...
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Існуючая скрабніца...
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Дзякуй, што ты абраў Cryptomator для абароны тваіх файлаў. Калі табе патрэбна дапамога, калі ласка, паглядзі нашы інструкцыі:
|
||||
@@ -437,6 +447,7 @@ vaultOptions.masterkey.showRecoveryKeyBtn=Паказаць ключ аднаўл
|
||||
vaultOptions.masterkey.recoverPasswordBtn=Скінуць пароль
|
||||
## Hub
|
||||
vaultOptions.hub=Аднаўленне
|
||||
vaultOptions.hub.convertBtn=Сканвэртаваць у скарбніцу з паролем
|
||||
|
||||
# Recovery Key
|
||||
## Display Recovery Key
|
||||
@@ -488,4 +499,9 @@ quit.forced.message=Некаторыя скарбніцы не магчыма з
|
||||
quit.forced.description=Замыканне скарбніц было заблакавана праз дзеючыя аперацыі альбо праз адчыненыя файлы. Ты можаш прымусова замкнуць скарбніцы, але гэта можа прывесці да страты незахаваных даных.
|
||||
quit.forced.forceAndQuitBtn=Прымусіць і выйсці
|
||||
|
||||
# Update Reminder
|
||||
# Update Reminder
|
||||
updateReminder.title=Праверка абнаўленняў
|
||||
updateReminder.message=Ці праверыць абнаўленні?
|
||||
updateReminder.notNow=Не цяпер
|
||||
updateReminder.yesOnce=Так, аднойчы
|
||||
updateReminder.yesAutomatically=Так, аўтаматычна
|
||||
@@ -20,6 +20,11 @@ error.description=Това е неочаквано за Криптоматор.
|
||||
error.hyperlink.lookup=Търсене на грешката
|
||||
error.hyperlink.report=Докладване на грешката
|
||||
error.technicalDetails=Подробности:
|
||||
error.existingSolutionDescription=Това е неочаквано за Криптоматор, но открихме съществуващо решение. Прочетете повече на следната препратка.
|
||||
error.hyperlink.solution=Преглеждане на решението
|
||||
error.lookupPermissionMessage=Криптоматор може да потърси решение за проблема. За целта ще направи заявка към базата от знания от вашия IP адрес.
|
||||
error.dismiss=Отхвърляне
|
||||
error.lookUpSolution=Търсене на решение
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=Хранилище
|
||||
@@ -35,10 +40,8 @@ traymenu.vault.reveal=Разкриване
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Добавяне на хранилище
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Ново хранилище
|
||||
addvaultwizard.welcome.existingButton=Отваряне на хранилище
|
||||
## New
|
||||
addvaultwizard.new.title=Добавяне на хранилище
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Изберете име на хранилището
|
||||
addvaultwizard.new.namePrompt=Наименование
|
||||
@@ -60,12 +63,18 @@ addvaultwizard.new.validCharacters.chars=Букви (напр. a, ж или 수)
|
||||
addvaultwizard.new.validCharacters.numbers=Числа
|
||||
addvaultwizard.new.validCharacters.dashes=Тире (%s) или долна черта (%s)
|
||||
### Expert Settings
|
||||
addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=Разширени настройки
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.invalid=Въведете стойност между 36 и 220 (по подразбиране 200)
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=Прочетете повече в документацията.
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.title=Максимална дължина на шифрованите имена на файлове
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.valid=Валидно
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Създаване
|
||||
addvaultwizard.new.generateRecoveryKeyChoice=Без парола няма да имате достъп до данните си. Желаете ли да бъде създаден ключ за възстановяване, в случай че загубите паролата си?
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Да, нека имам за всеки случай
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Не, няма да загубя паролата си
|
||||
### Information
|
||||
addvault.new.readme.storageLocation.fileName=ВАЖНО.rtf
|
||||
addvault.new.readme.storageLocation.1=⚠️ ФАЙЛОВЕ НА ХРАНИЛИЩЕ ⚠️
|
||||
addvault.new.readme.storageLocation.2=Това е местоположението на хранилището.
|
||||
addvault.new.readme.storageLocation.3=НЕДЕЙТЕ
|
||||
@@ -76,12 +85,14 @@ addvault.new.readme.storageLocation.7=1. Добавете това хранил
|
||||
addvault.new.readme.storageLocation.8=2. Отключете хранилището в Криптоматор.
|
||||
addvault.new.readme.storageLocation.9=3. Отворете местоположението на съдържанието чрез бутона „Разкриване“.
|
||||
addvault.new.readme.storageLocation.10=Ако имате нужда от помощ, посетете документацията: %s
|
||||
addvault.new.readme.accessLocation.fileName=ДОБРЕ ДОШЛИ.rtf
|
||||
addvault.new.readme.accessLocation.1=🔐️ ШИФРОВАН ДЯЛ 🔐️
|
||||
addvault.new.readme.accessLocation.2=Това е местоположението на съдържанието на хранилището.
|
||||
addvault.new.readme.accessLocation.3=Файловете, в този дял са шифроване от Криптоматор. Можете да работите с тях както с всеки друг диск или папка. Това е само разшифрован вариант на съдуржанието. Файловете остават шифровани на твърдия диск през цялото време.
|
||||
addvault.new.readme.accessLocation.4=При желание можете да премахнете този файл.
|
||||
## Existing
|
||||
addvaultwizard.existing.instruction=Изберете файла „vault.cryptomator“ от съществуващото хранилище. Но ако съществува файл „masterkey.cryptomator“, изберете него.
|
||||
addvaultwizard.existing.title=Добавяне на съществуващо хранилище
|
||||
addvaultwizard.existing.instruction=Изберете файла „vault.cryptomator“ от съществуващото хранилище, но ако има само файл „masterkey.cryptomator“, изберете него.
|
||||
addvaultwizard.existing.chooseBtn=Избиране…
|
||||
addvaultwizard.existing.filePickerTitle=Избор на файл на хранилището
|
||||
addvaultwizard.existing.filePickerMimeDesc=Хранилище на Криптоматор
|
||||
@@ -125,6 +136,11 @@ unlock.success.revealBtn=Разкриване на диска
|
||||
unlock.error.customPath.message=Хранилището не може да бъде монтирано в потребителския път
|
||||
unlock.error.customPath.description.notSupported=Ако искате да продължите да използвате потребитрлския път, отидете в настройките и изберете вид на дял, който поддържа потребителски пътища. В противен случай отидете в настройките на хранилището и изберете поддържана точка за монтиране.
|
||||
unlock.error.customPath.description.notExists=Потребителският път на монтиране не съществува. Създайте го в местната файлова система или го променете в настройките на хранилището.
|
||||
unlock.error.customPath.description.inUse=Буквата на диска или потребителският път на монтиране „%s“ е зает.
|
||||
unlock.error.customPath.description.hideawayNotDir=Временният, скрит файл „%3$s“, използван за отключване не може да бъде премахнат. Проверете файла и го изтройте ръчно.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Хранилището не може да бъде монтирано на „%s“. Опитайте отново или изберете друг път.
|
||||
unlock.error.customPath.description.notEmptyDir=Потребителският път на монтиране „%s“ не е празна папка. Изберете празна папка и опитайте отново.
|
||||
unlock.error.customPath.description.generic=Избрали сте потребителски път за монтиране на това хранилище, но при използването му възникна следната грешка: %2$s
|
||||
## Hub
|
||||
hub.noKeychain.message=Няма достъп до ключа на устройството
|
||||
hub.noKeychain.description=За да отключите хранилищата в Hub е необходим ключ за устройството, който се защитава с помощта на ключодържател. За да продължите, разрешете „%s“ и изберете ключодържателя в настройките.
|
||||
@@ -137,9 +153,10 @@ hub.auth.loginLink=Не сте пренасочени? Щракнете тук,
|
||||
hub.receive.message=Обработване на отговора…
|
||||
hub.receive.description=Криптоматор получава и обработва отговора от Hub. Изчакайте.
|
||||
### Register Device
|
||||
hub.register.message=Изисква се име на устройство
|
||||
hub.register.description=Изглежда, че това е първи достъп до Hub от това устройство. За да го разпознаете при разрешаване на достъпа, трябва да му дадете име.
|
||||
hub.register.message=Ново устройство
|
||||
hub.register.description=За пръв път посещавате Hub от това устройство. Удостоверете го, като използвате ключа на профила.
|
||||
hub.register.nameLabel=Име на устройството
|
||||
hub.register.invalidAccountKeyLabel=Неприемлив ключ на профила
|
||||
hub.register.occupiedMsg=Това име вече е заето
|
||||
hub.register.registerBtn=Потвърждаване
|
||||
### Registration Success
|
||||
@@ -151,30 +168,82 @@ hub.registerFailed.description=В процеса на именуване е до
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Отказан достъп
|
||||
hub.unauthorized.description=Устройството не е упълномощено за достъп до това хранилище. Поискайте достъп от собственика.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Необходимо действие
|
||||
hub.requireAccountInit.description.0=За да продължите завършене необходимите стъпки в
|
||||
hub.requireAccountInit.description.1=профила в Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Лиценза за Hub е недействителен
|
||||
hub.invalidLicense.description=Лиценза на екземпляра на Концентратора на Криптоматор който вие използвате е лиценз. Информирайте администратора на Концентратора, за да поднови или надгради лиценза.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
lock.forced.message=Грешка при заключване
|
||||
lock.forced.description=Хранилището „%s“ не може да бъде заключено, поради извършващо се действие или отворен файл. Можете принудително да го заключите, но прекъсването на действия на четене/писане в хранилището може да доведе до загуба на незапазени данни.
|
||||
lock.forced.retryBtn=Повторен опит
|
||||
lock.forced.forceBtn=Принудително заключване
|
||||
## Failure
|
||||
lock.fail.message=Грешка при заключване на хранилището
|
||||
lock.fail.description=Хранилището „%s“ не може да бъде заключено. Уверете се, че разполагате другаде с назапазената информация и че действията на четене/писане в хранилището са завършили. За да бъде затворено хранилището, трябва процесът на Криптоматор да бъда прекъснат.
|
||||
|
||||
# Migration
|
||||
migration.title=Надграждане на хранилище
|
||||
## Start
|
||||
migration.start.header=Надграждане на хранилището
|
||||
migration.start.text=За да може това ново издание на Криптоматор да отвори хранилището „%s“, то трябва да бъде надстроено до новия формат. Преди да го надстроите, трябва да знаете следното:
|
||||
migration.start.remarkUndone=Надграждането е еднопосочен процес.
|
||||
migration.start.remarkVersions=По-ранните издания на Криптоматор няма да могат да отварят надградените хранилища.
|
||||
migration.start.remarkCanRun=Трябва да сте сигурни, че всички устройства от които достъпвате хранилището могат да работят с това издание на Криптоматор.
|
||||
migration.start.remarkSynced=Трябва да сте сигурни, че хранилището е напълно синхронизирано между всички устройства, преди да пристъпите към надграждане.
|
||||
migration.start.confirm=Прочетох и разбрах гореизложената информация
|
||||
## Run
|
||||
migration.run.enterPassword=Въведете паролата за „%s“
|
||||
migration.run.startMigrationBtn=Надграждане на хранилището
|
||||
migration.run.progressHint=Ще отнеме малко време…
|
||||
## Success
|
||||
migration.success.nextStepsInstructions=Хранилището „%s“ е надградено.\nМожете да го отключите.
|
||||
migration.success.unlockNow=Отключване сега
|
||||
## Missing file system capabilities
|
||||
migration.error.missingFileSystemCapabilities.title=Неподдържана файлова система
|
||||
migration.error.missingFileSystemCapabilities.description=Не е започнато надграждане, защото хранилището се намира на неподдържана файлова система.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Файловата система не поддържа дълги имена на файлове.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Файловата система не поддържа дълги пътища.
|
||||
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Файловата система не дава права за четене.
|
||||
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Файловата система не дава права за писане.
|
||||
## Impossible
|
||||
migration.impossible.heading=Хранилището не е надградено
|
||||
migration.impossible.reason=Хранилището не може да бъде надградено автоматично, защото местоположението или точката за достъп са несъвместими.
|
||||
migration.impossible.moreInfo=Хранилището миже да бъде отворено с по-ранно издание на Криптоматор. За инструкции как ръчно да надстроите хранилището постете
|
||||
|
||||
# Health Check
|
||||
## Start
|
||||
health.title=Проверка на състоянието на „%s“
|
||||
health.intro.header=Проверка на състоянието
|
||||
health.intro.text=Проверката на състоянието е набор от проверки и евентуално поправки за проблеми във вътрешната структура на хранилището. Имайте предвид, че:
|
||||
health.intro.remarkSync=Трябва да сте сигурни, че хранилището е напълно синхронизирано между всички устройства, по този начин ще резрешите повечето проблеми.
|
||||
health.intro.remarkFix=Не всички проблеми могат да бъдат разрешени.
|
||||
health.intro.remarkBackup=Ако данните са повредени, само наличие на резервно копие ще помогне.
|
||||
health.intro.affirmation=Прочетох и разбрах гореизложената информация
|
||||
## Start Failure
|
||||
health.fail.header=Грешка при зареждане на настройките на хранилището
|
||||
health.fail.ioError=Грешка при достъп или четене на файла с настройките.
|
||||
health.fail.parseError=Грешка при разбор на файла с настройките на хранилището.
|
||||
health.fail.moreInfo=Повече информация
|
||||
## Check Selection
|
||||
health.checkList.description=Използвайте отметките отляво или бутоните отдолу.
|
||||
health.checkList.selectAllButton=Избор всичко
|
||||
health.checkList.deselectAllButton=Избор нищо
|
||||
health.check.runBatchBtn=Извършване на избраните
|
||||
## Detail view
|
||||
health.check.detail.noSelectedCheck=За резултати изберете завършила проверка от списъка от ляво.
|
||||
health.check.detail.checkScheduled=Извършването на проверката предстои.
|
||||
health.check.detail.checkRunning=Проверката в момента се извършва…
|
||||
health.check.detail.checkSkipped=Проверката не е избрана за извършване.
|
||||
health.check.detail.checkFinished=Проверката е завършена.
|
||||
health.check.detail.checkFinishedAndFound=Проверката е извършена. Прегледайте резултатите.
|
||||
health.check.detail.checkFailed=Проверката не е завършена поради грешка.
|
||||
health.check.detail.checkCancelled=Проверката е прекъсната.
|
||||
health.check.detail.listFilters.label=Филтър
|
||||
health.check.detail.fixAllSpecificBtn=Поправка всички от вида
|
||||
health.check.exportBtn=Отчет
|
||||
@@ -183,80 +252,269 @@ health.result.severityFilter.all=Сериозност - Всички
|
||||
health.result.severityFilter.good=Добре
|
||||
health.result.severityFilter.info=Информация
|
||||
health.result.severityFilter.warn=Предупреждение
|
||||
health.result.severityFilter.crit=Криточно
|
||||
health.result.severityFilter.crit=Критично
|
||||
health.result.severityTip.good=Сериозност: Добре\nСтруктурата на хранилището е наред.
|
||||
health.result.severityTip.info=Сериозност: Информация\nСтруктурата на хранилището е непокътната, препоръчва се поправка.
|
||||
health.result.severityTip.warn=Сериозност: Предупреждение\nСтруктурата на хранилището е нарушена, поправката е силно препоръчителна.
|
||||
health.result.severityTip.crit=Сериозност: Критично\nСтруктурата на хранилището е нарушена, установена е загуба на информация.
|
||||
health.result.fixStateFilter.all=Поправимост - Всички
|
||||
health.result.fixStateFilter.fixable=Поправими
|
||||
health.result.fixStateFilter.notFixable=Непоправими
|
||||
health.result.fixStateFilter.fixing=Поправяне…
|
||||
health.result.fixStateFilter.fixed=Поправени
|
||||
health.result.fixStateFilter.fixFailed=Грешка при поправка
|
||||
## Fix Application
|
||||
health.fix.fixBtn=Поправяне
|
||||
health.fix.successTip=Поправката е успешна
|
||||
health.fix.failTip=Грешка при поправката, вижте дневника за подробности
|
||||
|
||||
# Preferences
|
||||
preferences.title=Настройки
|
||||
## General
|
||||
preferences.general=Общи
|
||||
preferences.general.startHidden=Скриване на прозореца при отваряне на Криптоматор
|
||||
preferences.general.autoCloseVaults=Заключване на хранилищата при затваряне на приложението
|
||||
preferences.general.debugLogging=Дневник за отстраняване на дефекти
|
||||
preferences.general.debugDirectory=Файлове на дневниците
|
||||
preferences.general.autoStart=Отваряне на Криптоматор при старт на системата
|
||||
preferences.general.keychainBackend=Съхраняванеа паролите в
|
||||
## Interface
|
||||
preferences.interface.theme=Оформление
|
||||
preferences.interface=Външен вид
|
||||
preferences.interface.theme=Тема
|
||||
preferences.interface.theme.automatic=Автоматично
|
||||
preferences.interface.theme.dark=Тъмна
|
||||
preferences.interface.theme.light=Светла
|
||||
preferences.interface.unlockThemes=Отключване на тъмната тема
|
||||
preferences.interface.language=Език (изисква рестарт)
|
||||
preferences.interface.language.auto=Подразбиран
|
||||
preferences.interface.interfaceOrientation=Посока на текста
|
||||
preferences.interface.interfaceOrientation.ltr=От ляво на дясно
|
||||
preferences.interface.interfaceOrientation.rtl=От дясно на ляво
|
||||
preferences.interface.showMinimizeButton=Бутон за скриване
|
||||
preferences.interface.showTrayIcon=Икона в областта за известия (изисква рестарт)
|
||||
## Volume
|
||||
preferences.volume=Виртуален диск
|
||||
preferences.volume.type=Вид на тома
|
||||
preferences.volume.type.automatic=Автоматично
|
||||
preferences.volume.docsTooltip=Вижте документацията относно видовете томове.
|
||||
preferences.volume.fuseRestartRequired=За да бъдат приложени промените, Криптоматор трябва да бъде рестартиран.
|
||||
preferences.volume.tcp.port=Порт на TCP
|
||||
preferences.volume.supportedFeatures=Избрания вид на тома има следните възможности:
|
||||
preferences.volume.feature.mountAuto=Автоматичен избор на точка за монтиране
|
||||
preferences.volume.feature.mountToDir=Папка по избор като точка за монтиране
|
||||
preferences.volume.feature.mountToDriveLetter=Буква на диск като точка за монтиране
|
||||
preferences.volume.feature.mountFlags=Потребителски настройки на монтиране
|
||||
preferences.volume.feature.readOnly=Монтиране само за четене
|
||||
## Updates
|
||||
preferences.updates=Издания
|
||||
preferences.updates.currentVersion=Текущо издание: %s
|
||||
preferences.updates.autoUpdateCheck=Автоматична проверка за издания
|
||||
preferences.updates.checkNowBtn=Проверяване
|
||||
preferences.updates.updateAvailable=Налично е ново издание %s.
|
||||
## Contribution
|
||||
preferences.contribute=Подкрепете ни
|
||||
preferences.contribute.registeredFor=Сертификатът за дарение е регистриран на %s
|
||||
preferences.contribute.noCertificate=Ако подкрепите Криптоматор ще получите сертификат за дарение. Това е като лицензен ключ, но за готини хора, които използват безплатен софтуер. ;-)
|
||||
preferences.contribute.getCertificate=Все още нямате? Научете как да се сдобиете.
|
||||
preferences.contribute.promptText=Поставете тук кода на сертификата за дарение
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Относно
|
||||
|
||||
# Vault Statistics
|
||||
stats.title=Статистика на %s
|
||||
stats.cacheHitRate=Съотношения на попадения
|
||||
## Read
|
||||
stats.read.throughput.idle=Четене: бездействие
|
||||
stats.read.throughput.kibs=Четене: %.2f КБ/с
|
||||
stats.read.throughput.mibs=Четене: %.2f МБ/с
|
||||
stats.read.total.data.none=Прочетени данни: -
|
||||
stats.read.total.data.kib=Прочетени данни: %.1f КБ
|
||||
stats.read.total.data.mib=Прочетени данни: %.1f МБ
|
||||
stats.read.total.data.gib=Прочетени данни: %.1f ГБ
|
||||
stats.decr.total.data.none=Разшифровани данни: -
|
||||
stats.decr.total.data.kib=Разшифровани данни: %.1f КБ
|
||||
stats.decr.total.data.mib=Разшифровани данни: %.1f МБ
|
||||
stats.decr.total.data.gib=Разшифровани данни: %.1f ГБ
|
||||
stats.read.accessCount=Общо прочитания: %d
|
||||
## Write
|
||||
stats.write.throughput.idle=Писане: бездействие
|
||||
stats.write.throughput.kibs=Писане: %.2f КБ/с
|
||||
stats.write.throughput.mibs=Писане: %.2f МБ/с
|
||||
stats.write.total.data.none=Записани данни: -
|
||||
stats.write.total.data.kib=Записани данни: %.1f КБ
|
||||
stats.write.total.data.mib=Записани данни: %.1f МБ
|
||||
stats.write.total.data.gib=Записани данни: %.1f ГБ
|
||||
stats.encr.total.data.none=Шифровани данни: -
|
||||
stats.encr.total.data.kib=Шифровани данни: %.1f КБ
|
||||
stats.encr.total.data.mib=Шифровани данни: %.1f МБ
|
||||
stats.encr.total.data.gib=Шифровани данни: %.1f ГБ
|
||||
stats.write.accessCount=Общо записвания: %d
|
||||
|
||||
## Accesses
|
||||
stats.access.current=Достъпване: %d
|
||||
stats.access.total=Общо достъпвания: %d
|
||||
|
||||
|
||||
# Main Window
|
||||
main.closeBtn.tooltip=Затваряне
|
||||
main.minimizeBtn.tooltip=Скриване
|
||||
main.preferencesBtn.tooltip=Настройки
|
||||
main.debugModeEnabled.tooltip=Режимът за премахване на дефекти е включен
|
||||
main.supporterCertificateMissing.tooltip=Обмислете дарение
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Щракнете, за да добавите хранилище
|
||||
main.vaultlist.contextMenu.remove=Премахване…
|
||||
main.vaultlist.contextMenu.lock=Заключване
|
||||
main.vaultlist.contextMenu.unlock=Отключване…
|
||||
main.vaultlist.contextMenu.unlockNow=Отключване сега
|
||||
main.vaultlist.contextMenu.vaultoptions=Настройки на хранилището
|
||||
main.vaultlist.contextMenu.reveal=Разкриване на диска
|
||||
main.vaultlist.addVaultBtn=Добавяне на хранилище
|
||||
main.vaultlist.addVaultBtn=Добавяне
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Ново хранилище…
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Съществуващо хранилище…
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Благодарим ви, че избрахте Криптоматор, за да предпазвате файловете си. Ако имате нужда от съдействие прочетете ръководствата за започване на работа с приложението:
|
||||
### Locked
|
||||
main.vaultDetail.lockedStatus=ЗАКЛЮЧЕНО
|
||||
main.vaultDetail.unlockBtn=Отключване…
|
||||
main.vaultDetail.unlockNowBtn=Отключване сега
|
||||
main.vaultDetail.optionsBtn=Настройки на хранилището
|
||||
main.vaultDetail.passwordSavedInKeychain=Паролата е запазена
|
||||
### Unlocked
|
||||
main.vaultDetail.unlockedStatus=ОТКЛЮЧЕНО
|
||||
main.vaultDetail.accessLocation=Съдържанието на хранилището е достъпно тук:
|
||||
main.vaultDetail.revealBtn=Разкриване на диска
|
||||
main.vaultDetail.copyUri=Копиране на URI
|
||||
main.vaultDetail.lockBtn=Заключване
|
||||
main.vaultDetail.bytesPerSecondRead=Четене:
|
||||
main.vaultDetail.bytesPerSecondWritten=Писане:
|
||||
main.vaultDetail.throughput.idle=бездействие
|
||||
main.vaultDetail.throughput.kbps=%.1f КБ/с
|
||||
main.vaultDetail.throughput.mbps=%.1f МБ/с
|
||||
main.vaultDetail.stats=Статистики на хранилището
|
||||
main.vaultDetail.locateEncryptedFileBtn=Намиране на шифровани файлове
|
||||
main.vaultDetail.locateEncryptedFileBtn.tooltip=Изберете файл от хранилището, за да бъде намерено шифрованото му копие
|
||||
main.vaultDetail.encryptedPathsCopied=Пътищата са копирани!
|
||||
main.vaultDetail.filePickerTitle=Изберете файл от хранилището
|
||||
### Missing
|
||||
main.vaultDetail.missing.info=Криптоматор не намира хранилище на това място.
|
||||
main.vaultDetail.missing.recheck=Повторен опит
|
||||
main.vaultDetail.missing.remove=Премахване от списъка с хранилищата…
|
||||
main.vaultDetail.missing.changeLocation=Смяна на мястото на хранилището…
|
||||
### Needs Migration
|
||||
main.vaultDetail.migrateButton=Надграждане на хранилище
|
||||
main.vaultDetail.migratePrompt=Преди да можете да го достъпвате, хранилището трябва да бъде надградено до новия формат
|
||||
### Error
|
||||
main.vaultDetail.error.info=Грешка при зареждане на хранилището от диска.
|
||||
main.vaultDetail.error.reload=Презареждане
|
||||
main.vaultDetail.error.windowTitle=Грешка при зареждане на хранилще
|
||||
|
||||
# Wrong File Alert
|
||||
wrongFileAlert.title=Как да шифровате файлове
|
||||
wrongFileAlert.message=Пробвахте ли да шифровате файловете?
|
||||
wrongFileAlert.description=За тази цел Криптоматор създава том, достъпен от приложението за управление на файлове.
|
||||
wrongFileAlert.instruction.0=За да шифровета файлове следвайте тези стъпки:
|
||||
wrongFileAlert.instruction.1=1. Отключете хранилището.
|
||||
wrongFileAlert.instruction.2=2. Щракнете върху „Разкриване“, за да се покаже хранилището.
|
||||
wrongFileAlert.instruction.3=3. Добавете файловете в тома.
|
||||
wrongFileAlert.link=За допълнителна помощ, посетете
|
||||
|
||||
# Vault Options
|
||||
## General
|
||||
vaultOptions.general=Общи
|
||||
vaultOptions.general.vaultName=Наименование
|
||||
vaultOptions.general.autoLock.lockAfterTimePart1=Заключване при бездействие от
|
||||
vaultOptions.general.autoLock.lockAfterTimePart2=минути
|
||||
vaultOptions.general.unlockAfterStartup=Отключване на хранилището при старт на Криптоматор
|
||||
vaultOptions.general.actionAfterUnlock=След отключване
|
||||
vaultOptions.general.actionAfterUnlock.ignore=Без действие
|
||||
vaultOptions.general.actionAfterUnlock.reveal=Разкриване на диска
|
||||
vaultOptions.general.actionAfterUnlock.ask=Запитване
|
||||
vaultOptions.general.startHealthCheckBtn=Проверка на състоянието
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Монтиране
|
||||
vaultOptions.mount.info=Възможностите зависят от вида на избрания том.
|
||||
vaultOptions.mount.linkToPreferences=Настройки на виртуалния диск
|
||||
vaultOptions.mount.readonly=Само за четене
|
||||
vaultOptions.mount.customMountFlags=Флагове на монтиране
|
||||
vaultOptions.mount.winDriveLetterOccupied=използвана
|
||||
vaultOptions.mount.mountPoint=Точка на монтиране
|
||||
vaultOptions.mount.mountPoint.auto=Автоматично избиране на подходящо място
|
||||
vaultOptions.mount.mountPoint.driveLetter=Използване на определената буква на диска
|
||||
vaultOptions.mount.mountPoint.custom=Използване на избрана папка
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Избиране…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Избиране на папка
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Парола
|
||||
vaultOptions.masterkey.changePasswordBtn=Промяна на парола
|
||||
vaultOptions.masterkey.forgetSavedPasswordBtn=Забравяне на запазената парола
|
||||
vaultOptions.masterkey.recoveryKeyExplanation=Ключът за възстановяване е единствения начин да си върнете достъпа да хранилището ако загубите паролата.
|
||||
vaultOptions.masterkey.showRecoveryKeyBtn=Показване на ключ за възстановяване
|
||||
vaultOptions.masterkey.recoverPasswordBtn=Нулиране на паролата
|
||||
## Hub
|
||||
vaultOptions.hub=Възстановяване
|
||||
vaultOptions.hub.convertInfo=В случай на спешност можете да използвате ключа за възстановяване, за да преобразувате това хранилище от хранилище от концентратор в хранилище с парола.
|
||||
vaultOptions.hub.convertBtn=Преобразуване в хранилище с парола
|
||||
|
||||
# Recovery Key
|
||||
## Display Recovery Key
|
||||
recoveryKey.display.title=Показване на ключ за възстановяване
|
||||
recoveryKey.create.message=Изисква се парола
|
||||
recoveryKey.create.description=За да видите ключа за възстановяване въведете паролата за „%s“.
|
||||
recoveryKey.display.description=Следния ключ за възстановязане може да бъде използван да си върнете достъпа до „%s“:
|
||||
recoveryKey.display.StorageHints=Пазете го на много сигурно място, например:\n • Пазете го в софтуер за управление на пароли\n • Запишете го на USB памет\n • Отпечатайте го на хартия
|
||||
## Reset Password
|
||||
### Enter Recovery Key
|
||||
recoveryKey.recover.title=Нулиране на паролата
|
||||
recoveryKey.recover.prompt=Въведете ключа за възстановяване на „%s“:
|
||||
recoveryKey.recover.correctKey=Верен ключ за възстановяване
|
||||
recoveryKey.recover.wrongKey=Този ключ за възстановяване принадлежи на друго хранилище
|
||||
recoveryKey.recover.invalidKey=Неприемлив ключ за възстановяване
|
||||
recoveryKey.printout.heading=Ключ за възстановяване на Криптоматор\n„%s“\n
|
||||
### Reset Password
|
||||
recoveryKey.recover.resetBtn=Нулиране
|
||||
### Recovery Key Password Reset Success
|
||||
recoveryKey.recover.resetSuccess.message=Паролата е променена
|
||||
recoveryKey.recover.resetSuccess.description=Можете да отключите хранилището с новата парола.
|
||||
|
||||
# Convert Vault
|
||||
convertVault.title=Преобразуване на хранилище
|
||||
convertVault.convert.convertBtn.before=Преобразуване
|
||||
convertVault.convert.convertBtn.processing=Преобразуване…
|
||||
convertVault.success.message=Преобразуване е успешно
|
||||
convertVault.hubToPassword.success.description=Можете да отключите хранилището с избраната парола без необходимост от достъп до Концентратор.
|
||||
|
||||
# New Password
|
||||
newPassword.promptText=Въведете нова парола
|
||||
newPassword.reenterPassword=Потвърдете новата парола
|
||||
newPassword.passwordsMatch=Паролите съвпадат!
|
||||
newPassword.passwordsDoNotMatch=Паролите не съвпадат
|
||||
passwordStrength.messageLabel.tooShort=Използвайте минимум %d знака
|
||||
passwordStrength.messageLabel.0=Много слаба
|
||||
passwordStrength.messageLabel.1=Слаба
|
||||
passwordStrength.messageLabel.2=Задоволителна
|
||||
passwordStrength.messageLabel.3=Добра
|
||||
passwordStrength.messageLabel.4=Много добра
|
||||
|
||||
# Quit
|
||||
quit.title=Изход от приложението
|
||||
quit.message=Има отключени хранилища
|
||||
quit.description=Потвърдете излизане от приложението. За да предотврати загуба на и формация Криптоматор ще заключи отворените хранилища.
|
||||
quit.lockAndQuitBtn=Заключване и изход
|
||||
|
||||
# Forced Quit
|
||||
quit.forced.message=Някои от хранилищата не могат да бъдат заключени
|
||||
quit.forced.description=Хранилището не може да бъде заключено, поради извършващо се действие или отворен файл. Можете принудително да заключите останалите хранилища, но прекъсването на входно-изходни процеси може да доведе до загуба на незапазени данни.
|
||||
quit.forced.forceAndQuitBtn=Прекъсване и изход
|
||||
|
||||
# Update Reminder
|
||||
# Update Reminder
|
||||
updateReminder.title=Проверка за обновяване
|
||||
updateReminder.message=Проверяване за обновяване?
|
||||
updateReminder.description=Получавайте нови възможности на приложението, поправки на дефекти и подобрения на сигурността. Препоръчваме ви автоматичното обновяване.
|
||||
updateReminder.notNow=Не сега
|
||||
updateReminder.yesOnce=Да, веднъж
|
||||
updateReminder.yesAutomatically=Да, автоматично
|
||||
@@ -33,9 +33,6 @@ traymenu.vault.lock=লক করুন
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=ভোল্ট যুক্ত করুন
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=নতুন ভোল্ট তৈরি করুন
|
||||
addvaultwizard.welcome.existingButton=বিদ্যমান কোনো ভোল্ট খুলুন
|
||||
## New
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=ভোল্ট এর একটি নাম দেন
|
||||
@@ -96,6 +93,7 @@ hub.register.registerBtn=নিশ্চিত করুন
|
||||
### Registration Success
|
||||
### Registration Failed
|
||||
### Unauthorized
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
|
||||
# Lock
|
||||
@@ -139,7 +137,6 @@ lock.forced.retryBtn=পুনরায় চেষ্টা করুন
|
||||
main.closeBtn.tooltip=বন্ধ করুন
|
||||
## Vault List
|
||||
main.vaultlist.contextMenu.lock=লক করুন
|
||||
main.vaultlist.addVaultBtn=ভোল্ট যুক্ত করুন
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
### Locked
|
||||
|
||||
@@ -31,9 +31,6 @@ traymenu.vault.reveal=Otkrij
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Dodaj sef
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Kreiraj novi sef
|
||||
addvaultwizard.welcome.existingButton=Otvori postojeći sef
|
||||
## New
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Izaberi naziv za sef
|
||||
@@ -112,6 +109,7 @@ hub.register.registerBtn=Potvrdi
|
||||
### Registration Success
|
||||
### Registration Failed
|
||||
### Unauthorized
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
|
||||
# Lock
|
||||
@@ -219,7 +217,6 @@ main.vaultlist.contextMenu.unlock=Otključaj…
|
||||
main.vaultlist.contextMenu.unlockNow=Otključaj sada
|
||||
main.vaultlist.contextMenu.vaultoptions=Pokaži opcije sefa
|
||||
main.vaultlist.contextMenu.reveal=Otkrij pogon
|
||||
main.vaultlist.addVaultBtn=Dodaj sef
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Hvala što ste izabrali Cryptomator za zaštitu podataka. Ako vam je potrebna pomoć, pogledajte naše vodiče za početak:
|
||||
|
||||
@@ -22,6 +22,9 @@ error.hyperlink.report=Notifica aquest error
|
||||
error.technicalDetails=Detalls:
|
||||
error.existingSolutionDescription=Cryptomator no esperava que això ocorreguera. Però hem trobat una solució per a aquest error. Per favor, done una ullada al següent enllaç.
|
||||
error.hyperlink.solution=Buscar la solució
|
||||
error.lookupPermissionMessage=Cryptomator pot cercar una solució a aquest problema en línia. S'enviarà una petició a la nostra base de dades de problemes des de la teva adreça IP.
|
||||
error.dismiss=Descartar
|
||||
error.lookUpSolution=Buscar Solució
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=Caixa forta
|
||||
@@ -37,10 +40,8 @@ traymenu.vault.reveal=Mostra
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Afegir una caixa forta
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Crea una caixa forta nova
|
||||
addvaultwizard.welcome.existingButton=Obri una caixa forta existent
|
||||
## New
|
||||
addvaultwizard.new.title=Afegeix nova caixa forta
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Introduiu el nom de la caixa forta
|
||||
addvaultwizard.new.namePrompt=Nom de la caixa forta
|
||||
@@ -62,6 +63,11 @@ addvaultwizard.new.validCharacters.chars=Caràcters que formen paraules (ex.: a,
|
||||
addvaultwizard.new.validCharacters.numbers=Nombres
|
||||
addvaultwizard.new.validCharacters.dashes=Guionet (%s) o guió baix (%s)
|
||||
### Expert Settings
|
||||
addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=Habilitar mode expert
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.invalid=Introduir un valor entre 36 i 220 (per defecte 220)
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=Obre la documentació per saber-ne més.
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.title=Llargària màxima dels fitxers xifrats
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.valid=Vàlid
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Crea la caixa forta
|
||||
addvaultwizard.new.generateRecoveryKeyChoice=No podreu accedir a les vostres dades sense la contrasenya. Voleu crear una clau de recuperació en cas perdre la vostra contrasenya?
|
||||
@@ -85,6 +91,7 @@ addvault.new.readme.accessLocation.2=Aquesta és la ubicació d'accès de la vos
|
||||
addvault.new.readme.accessLocation.3=Cryptomator xifra tots els fitxers afegits a aquest volum. Podeu treballar en aquest volum com en qualsevol altra unitat o carpeta. La vista mostra el contingut desxifrat però els fitxers sempre estan xifrats en el vostre disc dur.
|
||||
addvault.new.readme.accessLocation.4=Pots esborrar aquest fitxer si vols.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Afegeix una caixa forta existent
|
||||
addvaultwizard.existing.instruction=Selecciona el fitxer "vault.cryptomator" de la teva caixa forta. Si només existeix un fitxer anomenat "masterkey.cryptomator", selecciona aquest.
|
||||
addvaultwizard.existing.chooseBtn=Trieu…
|
||||
addvaultwizard.existing.filePickerTitle=Selecciona el fitxer de la Caixa forta
|
||||
@@ -129,6 +136,11 @@ unlock.success.revealBtn=Mostra la unitat
|
||||
unlock.error.customPath.message=No es pot muntar la caixa forta en la ruta personalitzada
|
||||
unlock.error.customPath.description.notSupported=Si vol continuar fent servir una ruta personalitzada, vagi si us plau a Preferències i seleccioni un tipus de volum que la suporti. Altrament, vagi a les opcions de la caixa forta i escolli un punt de muntatge suportat.
|
||||
unlock.error.customPath.description.notExists=La ruta de muntatge personalitzada no existeix. Creï-la o canviï-la en les opcions de la caixa forta.
|
||||
unlock.error.customPath.description.inUse=La unitat o el punt de muntatge "%s" ja és en ús.
|
||||
unlock.error.customPath.description.hideawayNotDir=El fitxer temporal "%3$s" utilitzat per desbloquejar no es pot esborrar. Revisi si us plau aquest fitxer i esborri'l manualment.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=La caixa forta no es pot muntar en "%s". Intenteu-ho de nou o escolliu un destí diferent, si us plau.
|
||||
unlock.error.customPath.description.notEmptyDir=El punt de muntatge "%s" no és una carpeta buida. Trieu una carpeta buida i proveu-ho de nou.
|
||||
unlock.error.customPath.description.generic=Al anar a desar la caixa forta en el lloc indicat ha fallat amb el missatge: %2$s
|
||||
## Hub
|
||||
hub.noKeychain.message=No es pot accedir a la clau del dispositiu
|
||||
hub.noKeychain.description=Per poder desblocar caixes fortes del Hub es requereix la clau d'un dispositiu, que s'emmagatzema de forma segura en un clauer. Per continuar, habiliti "%s" i seleccioni un clauer en les Preferències.
|
||||
@@ -141,8 +153,7 @@ hub.auth.loginLink=No heu estat redirigit? Feu clic aquí per a obrir-la.
|
||||
hub.receive.message=S'està processant la resposta…
|
||||
hub.receive.description=Cryptomator està rebent i processant la resposta del Hub. Espereu, si us plau.
|
||||
### Register Device
|
||||
hub.register.message=Cal un nom de dispositiu
|
||||
hub.register.description=Sembla que és el primer accés al Hub des d'aquest dispositiu. Per identificar-lo i poder autoritzar l'accés necessiteu anomenar aquest dispositiu.
|
||||
hub.register.message=Nou dispositiu
|
||||
hub.register.nameLabel=Nom del dispositiu
|
||||
hub.register.occupiedMsg=El nom ja està en ús
|
||||
hub.register.registerBtn=Confirma
|
||||
@@ -155,6 +166,7 @@ hub.registerFailed.description=S'ha produït un error en el procés de nomenamen
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Accés denegat
|
||||
hub.unauthorized.description=El vostre dispositiu no ha estat encara autoritzat a accedir a aquesta caixa forta. Demaneu autorització al propietari.
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=La llicència del Hub no és vàlida
|
||||
hub.invalidLicense.description=Aquest Cryptomator Hub no té una llicència vàlida. Informa si us plau a l'administrador perquè actualitzi o renovi la llicència.
|
||||
@@ -276,8 +288,10 @@ preferences.interface.showMinimizeButton=Mostra el botó 'minimitzar'
|
||||
preferences.interface.showTrayIcon=Mostra la icona en la barra (cal reiniciar)
|
||||
## Volume
|
||||
preferences.volume=Unitat virtual
|
||||
preferences.volume.type=Tipus de volum
|
||||
preferences.volume.type.automatic=Automàtic
|
||||
preferences.volume.docsTooltip=Obre la documentació per aprendre més sobre els diferents tipus de volums.
|
||||
preferences.volume.fuseRestartRequired=Per aplicar els canvis Cryptomator necessita reiniciar-se.
|
||||
preferences.volume.tcp.port=Port TCP
|
||||
preferences.volume.supportedFeatures=El tipus de volum escollit suporta les següents característiques:
|
||||
preferences.volume.feature.mountAuto=Selecció automàtica del punt de muntatge
|
||||
@@ -351,7 +365,9 @@ main.vaultlist.contextMenu.unlock=Desbloca…
|
||||
main.vaultlist.contextMenu.unlockNow=Desbloqueja ara
|
||||
main.vaultlist.contextMenu.vaultoptions=Opcions de la caixa forta
|
||||
main.vaultlist.contextMenu.reveal=Mostra la unitat
|
||||
main.vaultlist.addVaultBtn=Afegir una caixa forta
|
||||
main.vaultlist.addVaultBtn=Afegir
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Nova caixa forta...
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Caixa forta existent...
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Gràcies per escollir Cryptomator per protegir els vostres fitxers. Si vos cal ajuda, llegiu les nostres guies per donar els Primers passos:
|
||||
@@ -434,6 +450,9 @@ vaultOptions.masterkey.recoveryKeyExplanation=La clau de recuperació és l'unic
|
||||
vaultOptions.masterkey.showRecoveryKeyBtn=Mostra la clau de recuperació
|
||||
vaultOptions.masterkey.recoverPasswordBtn=Canviar contrasenya
|
||||
## Hub
|
||||
vaultOptions.hub=Recuperació
|
||||
vaultOptions.hub.convertInfo=En cas d'emergència pot fer servir la clau de recuperació per convertir aquesta caixa forta de Hub a una caixa forta amb paraula clau.
|
||||
vaultOptions.hub.convertBtn=Convertir a una caixa forta amb paraula clau
|
||||
|
||||
# Recovery Key
|
||||
## Display Recovery Key
|
||||
@@ -445,6 +464,7 @@ recoveryKey.display.StorageHints=Conserveu-la en un lloc molt segur. P. ex.:\n
|
||||
## Reset Password
|
||||
### Enter Recovery Key
|
||||
recoveryKey.recover.title=Canviar contrasenya
|
||||
recoveryKey.recover.prompt=Introduir la clau de recuperació per "%s":
|
||||
recoveryKey.recover.correctKey=La clau de recuperació és vàlida
|
||||
recoveryKey.recover.wrongKey=Aquesta clau de recuperació pertany a una caixa forta diferent
|
||||
recoveryKey.recover.invalidKey=Aquesta clau de recuperació no és vàlida
|
||||
@@ -456,6 +476,11 @@ recoveryKey.recover.resetSuccess.message=S'ha modificat la contrasenya correctam
|
||||
recoveryKey.recover.resetSuccess.description=Pots desbloquejar la caixa forta amb la nova contrasenya.
|
||||
|
||||
# Convert Vault
|
||||
convertVault.title=Convertir la caixa forta
|
||||
convertVault.convert.convertBtn.before=Convertir
|
||||
convertVault.convert.convertBtn.processing=Convertint…
|
||||
convertVault.success.message=Conversió correcta
|
||||
convertVault.hubToPassword.success.description=Ara pot desbloquejar la caixa forta amb la paraula clau sense haver d'accedir al Hub.
|
||||
|
||||
# New Password
|
||||
newPassword.promptText=Introdueix una contrasenya nova
|
||||
@@ -480,4 +505,10 @@ quit.forced.message=Algunes caixes fortes no s'han pogut bloquejar
|
||||
quit.forced.description=No s'ha pogut blocar la caixa forta perquè hi ha operacions pendents o fitxers oberts. Podeu forçar-ne el blocatge, però heu de saber que interrompre l'entrada/sortida pot produir la pèrdua de dades.
|
||||
quit.forced.forceAndQuitBtn=Forçar i sortir
|
||||
|
||||
# Update Reminder
|
||||
# Update Reminder
|
||||
updateReminder.title=Comprovar actualitzacions
|
||||
updateReminder.message=Comprova si hi ha actualitzacions?
|
||||
updateReminder.description=Per estar actualitzat amb funcions noves, correccions d'errors i millores de seguretat recomanem comprovar les actualitzacions automàticament.
|
||||
updateReminder.notNow=Ara no
|
||||
updateReminder.yesOnce=Sí, una vegada
|
||||
updateReminder.yesAutomatically=Sí, automàticament
|
||||
@@ -38,9 +38,6 @@ traymenu.vault.reveal=Zobrazit
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Přidat trezor
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Vytvořit nový trezor
|
||||
addvaultwizard.welcome.existingButton=Otevřít existující trezor
|
||||
## New
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Zvolte jméno trezoru
|
||||
@@ -145,7 +142,6 @@ hub.auth.loginLink=Nebyli jste přesměrováni? Klikněte zde pro otevření.
|
||||
### Receive Key
|
||||
hub.receive.message=Zpracovávání odezvy…
|
||||
### Register Device
|
||||
hub.register.message=Je vyžadován název zařízení
|
||||
hub.register.nameLabel=Název zařízení
|
||||
hub.register.occupiedMsg=Jméno je již používáno
|
||||
hub.register.registerBtn=Potvrdit
|
||||
@@ -157,6 +153,7 @@ hub.registerFailed.message=Pojmenování zařízení se nezdařilo
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Přístup odepřen
|
||||
hub.unauthorized.description=Vaše zařízení dosud nebylo oprávněno k přístupu k tomuto trezoru. Požádejte vlastníka trezoru, aby jej autorizoval.
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Licence Hubu je neplatná
|
||||
|
||||
@@ -341,7 +338,7 @@ main.vaultlist.contextMenu.unlock=Odemknout…
|
||||
main.vaultlist.contextMenu.unlockNow=Odemknout nyní
|
||||
main.vaultlist.contextMenu.vaultoptions=Zobrazit možnosti trezoru
|
||||
main.vaultlist.contextMenu.reveal=Zobrazit jednotku
|
||||
main.vaultlist.addVaultBtn=Přidat trezor
|
||||
main.vaultlist.addVaultBtn=Přidat
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Děkujeme, že jste si vybrali Cryptomator pro ochranu vašich souborů. Pokud potřebujete pomoc, podívejte se na naše návody:
|
||||
|
||||
@@ -22,6 +22,9 @@ error.hyperlink.report=Rapportér denne fejl
|
||||
error.technicalDetails=Detaljer:
|
||||
error.existingSolutionDescription=Cryptomator forventede ikke, at dette ville ske. Men vi har fundet en eksisterende løsning på denne fejl. Tag et kig på følgende link.
|
||||
error.hyperlink.solution=Tjek løsningen
|
||||
error.lookupPermissionMessage=Cryptomator kan finde en løsning på dette problem online. Dette vil sende en anmodning til vores problemdatabase fra din IP-adresse.
|
||||
error.dismiss=Luk
|
||||
error.lookUpSolution=Slå løsning op
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=Boks
|
||||
@@ -37,10 +40,8 @@ traymenu.vault.reveal=Vis
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Tilføj boks
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Opret ny boks
|
||||
addvaultwizard.welcome.existingButton=Open eksisterende boks
|
||||
## New
|
||||
addvaultwizard.new.title=Tilføj Ny Boks
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Vælg et navn til boksen
|
||||
addvaultwizard.new.namePrompt=Boks-navn
|
||||
@@ -90,6 +91,7 @@ addvault.new.readme.accessLocation.2=Det er her indholdet af din boks tilgås.
|
||||
addvault.new.readme.accessLocation.3=Filer du tilføjer til dette drev vil blive krypteret af Cryptomator. Du har arbejde med filerne ligesom enhver anden fil/mappe. Dette er blot en dekrypteret visning af indholdet. Dine filer er stadig krypterede på din harddisk hele tiden.
|
||||
addvault.new.readme.accessLocation.4=Fjern denne fil hvis du har lyst.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Tilføj Eksisterende Boks
|
||||
addvaultwizard.existing.instruction=Vælgt filen "vault.cryptomator" i mappen med dine boks-filer. Hvis der kun findes en fil med navnet "masterkey.cryptomator", skal du vælge den i stedet.
|
||||
addvaultwizard.existing.chooseBtn=Vælg…
|
||||
addvaultwizard.existing.filePickerTitle=Vælg boks-fil
|
||||
@@ -134,6 +136,11 @@ unlock.success.revealBtn=Vis drev
|
||||
unlock.error.customPath.message=Kan ikke montere boks til brugerdefineret sti
|
||||
unlock.error.customPath.description.notSupported=Hvis du ønsker at fortsætte med at bruge den brugerdefinerede sti, skal du gå til præferencer og vælge en type drev der understøtter det. Hvis ikke, skal du gå til boksens indstillinger og vælge et understøttet monteringspunkt.
|
||||
unlock.error.customPath.description.notExists=Den brugerdefinerede monteringssti eksisterer ikke. Opret den enten i dit lokale filsystem eller skift monteringssti i boksens indstillinger.
|
||||
unlock.error.customPath.description.inUse=Drevbogstavet eller den brugerdefinerede monteringssti "%s" er allerede i brug.
|
||||
unlock.error.customPath.description.hideawayNotDir=Den midlertidige, skjulte fil "%3$s", der blev brugt til oplåsning, kunne ikke fjernes. Kontroller filen og slet den manuelt.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Din boks kunne ikke monteres på stien "%s". Prøv venligst igen eller vælg en anden sti.
|
||||
unlock.error.customPath.description.notEmptyDir=Den brugerdefinerede monteringssti "%s" er ikke en tom mappe. Vælg venligst en tom mappe og prøv igen.
|
||||
unlock.error.customPath.description.generic=Du har valgt en brugerdefineret monteringssti til denne boks, men det mislykkedes med følgende besked: %2$s
|
||||
## Hub
|
||||
hub.noKeychain.message=Kan ikke tilgå enhedsnøgle
|
||||
hub.noKeychain.description=En enhedsnøgle er påkrævet for at låse Hub bokse op. Enhedsnøglen er sikret i en nøglering. For at fortsætte, aktivér “%s” og vælg en nøglering i indstillingerne.
|
||||
@@ -146,8 +153,7 @@ hub.auth.loginLink=Ingen omdirigering? Klik her for at åbne den.
|
||||
hub.receive.message=Behandler svar…
|
||||
hub.receive.description=Cryptomator modtager og behandler svaret fra hubben. Vent venligst.
|
||||
### Register Device
|
||||
hub.register.message=Enheds-navn krævet
|
||||
hub.register.description=Det ser ud til at dette er første gang denne enhed tilgår hubben. For at identificere denne enhed, skal du navngive den.
|
||||
hub.register.message=Ny Enhed
|
||||
hub.register.nameLabel=Enheds-navn
|
||||
hub.register.occupiedMsg=Navnet er allerede i brug
|
||||
hub.register.registerBtn=Bekræft
|
||||
@@ -160,6 +166,7 @@ hub.registerFailed.description=Der opstod en fejl i navngivnings-processen. Kig
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Adgang nægtet
|
||||
hub.unauthorized.description=Din enhed er endnu ikke blevet godkendt til at få adgang til denne boks. Spørg boks-ejeren om godkendelse.
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Ugyldig Hub-licens
|
||||
hub.invalidLicense.description=Din Cryptomator Hub har en ugyldig licens. Få venligst en Hub administrator til at opgradere eller forny licensen.
|
||||
@@ -358,7 +365,9 @@ main.vaultlist.contextMenu.unlock=Lås op…
|
||||
main.vaultlist.contextMenu.unlockNow=Lås op nu
|
||||
main.vaultlist.contextMenu.vaultoptions=Vis boksindstillinger
|
||||
main.vaultlist.contextMenu.reveal=Vis drev
|
||||
main.vaultlist.addVaultBtn=Tilføj boks
|
||||
main.vaultlist.addVaultBtn=Tilføj
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Ny Boks...
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Eksisterende Boks...
|
||||
## 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:
|
||||
|
||||
@@ -40,10 +40,8 @@ traymenu.vault.reveal=Anzeigen
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Tresor hinzufügen
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Neuen Tresor erstellen
|
||||
addvaultwizard.welcome.existingButton=Bestehenden Tresor öffnen
|
||||
## New
|
||||
addvaultwizard.new.title=Neuen Tresor hinzufügen
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Wähle einen Namen für den Tresor
|
||||
addvaultwizard.new.namePrompt=Tresorname
|
||||
@@ -93,6 +91,7 @@ addvault.new.readme.accessLocation.2=Dies ist der Zugangsort deines Tresors.
|
||||
addvault.new.readme.accessLocation.3=Alle zu diesem Laufwerk hinzugefügten Dateien werden von Cryptomator verschlüsselt. Du kannst mit diesem arbeiten wie mit jedem anderen Laufwerk bzw. Ordner. Dies ist lediglich eine unverschlüsselte Ansicht des Laufwerkinhalts; auf deiner Festplatte bleiben deine Dateien weiterhin verschlüsselt.
|
||||
addvault.new.readme.accessLocation.4=Du kannst diese Datei löschen.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Bestehenden Tresor hinzufügen
|
||||
addvaultwizard.existing.instruction=Wähle die Datei „vault.cryptomator“ deines bestehenden Tresors aus. Falls nur eine Datei mit der Bezeichnung „masterkey.cryptomator“ vorhanden ist, nutze stattdessen diese.
|
||||
addvaultwizard.existing.chooseBtn=Durchsuchen …
|
||||
addvaultwizard.existing.filePickerTitle=Tresordatei auswählen
|
||||
@@ -138,9 +137,9 @@ unlock.error.customPath.message=Tresor kann nicht in benutzerdefinierten Pfad ei
|
||||
unlock.error.customPath.description.notSupported=Wenn du weiterhin den benutzerdefinierten Pfad verwenden möchtest, öffne die allgemeinen Einstellungen und wähle einen unterstützten Laufwerkstyp. Andernfalls gehe zu den Tresor-Optionen und wähle einen unterstützten Einhängepunkt.
|
||||
unlock.error.customPath.description.notExists=Der benutzerdefinierte Einhängepunkt existiert nicht. Erstelle ihn in deinem lokalen Dateisystem oder ändere ihn in den Tresor-Optionen.
|
||||
unlock.error.customPath.description.inUse=Der Laufwerksbuchstabe oder benutzerdefinierte Einhängepunkt „%s“ wird bereits verwendet.
|
||||
unlock.error.customPath.description.hideawayNotDir=Die temporäre, versteckte Datei "%3$s", die für das Entsperren verwendet wurde, konnte nicht entfernt werden. Bitte überprüfe die Datei und lösche sie manuell.
|
||||
unlock.error.customPath.description.hideawayNotDir=Die temporäre, versteckte Datei „%3$s“, die für das Entsperren verwendet wurde, konnte nicht entfernt werden. Bitte überprüfe die Datei und lösche sie manuell.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Dein Tresor konnte nicht in den Pfad „%s“ eingehängt werden. Bitte versuche es erneut oder wähle einen anderen Pfad aus.
|
||||
unlock.error.customPath.description.notEmptyDir=Der benutzerdefinierte Einhängepunkt "%s" ist kein leerer Ordner. Bitte wähle einen leeren Ordner und versuche es erneut.
|
||||
unlock.error.customPath.description.notEmptyDir=Der benutzerdefinierte Einhängepunkt „%s“ ist kein leerer Ordner. Bitte wähle einen leeren Ordner und versuche es erneut.
|
||||
unlock.error.customPath.description.generic=Du hast für diesen Tresor einen benutzerdefinierten Einhängepunkt ausgewählt, aber dessen Verwendung ist mit folgender Meldung fehlgeschlagen: %2$s
|
||||
## Hub
|
||||
hub.noKeychain.message=Zugriff auf Geräteschlüssel nicht möglich
|
||||
@@ -154,9 +153,10 @@ hub.auth.loginLink=Keine Weiterleitung? Zum Öffnen hier anklicken.
|
||||
hub.receive.message=Antwort wird verarbeitet …
|
||||
hub.receive.description=Cryptomator empfängt und verarbeitet gerade die Antwort vom Hub. Bitte warten.
|
||||
### Register Device
|
||||
hub.register.message=Gerätename erforderlich
|
||||
hub.register.description=Dies scheint der erste Hub-Zugriff von diesem Gerät zu sein. Um es für die Zugriffsberechtigung zu identifizieren, mussst du diesem Gerät einen Namen geben.
|
||||
hub.register.message=Neues Gerät
|
||||
hub.register.description=Dies ist der erste Hub-Zugriff von diesem Gerät. Bitte autorisiere dich mit deinem Account Key.
|
||||
hub.register.nameLabel=Gerätename
|
||||
hub.register.invalidAccountKeyLabel=Ungültiger Account Key
|
||||
hub.register.occupiedMsg=Name bereits in Verwendung
|
||||
hub.register.registerBtn=Bestätigen
|
||||
### Registration Success
|
||||
@@ -168,6 +168,11 @@ hub.registerFailed.description=Während der Namensvergabe wurde ein Fehler ausge
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Zugriff verweigert
|
||||
hub.unauthorized.description=Dein Gerät wurde noch nicht für den Zugriff auf diesen Tresor autorisiert. Bitte den Tresorbesitzer, dein Gerät zu autorisieren.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Aktion erforderlich
|
||||
hub.requireAccountInit.description.0=Um fortzufahren, führe bitte die erforderlichen Schritte in deinem
|
||||
hub.requireAccountInit.description.1=Hub-Benutzerprofil
|
||||
hub.requireAccountInit.description.2=aus.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Hub-Lizenz ungültig
|
||||
hub.invalidLicense.description=Die Lizenz deiner Cryptomator-Hub-Instanz ist ungültig. Bitte informiere deinen Hub-Administrator, um die Lizenz zu erweitern oder zu erneuern.
|
||||
@@ -366,7 +371,9 @@ main.vaultlist.contextMenu.unlock=Entsperren …
|
||||
main.vaultlist.contextMenu.unlockNow=Jetzt entsperren
|
||||
main.vaultlist.contextMenu.vaultoptions=Tresoroptionen anzeigen
|
||||
main.vaultlist.contextMenu.reveal=Laufwerk anzeigen
|
||||
main.vaultlist.addVaultBtn=Tresor hinzufügen
|
||||
main.vaultlist.addVaultBtn=Hinzufügen
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Neuer Tresor …
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Bestehender Tresor …
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Danke, dass du zum Schutz deiner Dateien Cryptomator gewählt hast. Falls du Hilfe brauchst, schau dir unsere Anleitungen an:
|
||||
|
||||
@@ -40,10 +40,8 @@ traymenu.vault.reveal=Αποκάλυψη
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Προσθήκη Κρύπτης
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Δημιουργία Νέας Κρύπτης
|
||||
addvaultwizard.welcome.existingButton=Άνοιγμα Υπάρχοντος Κρύπτης
|
||||
## New
|
||||
addvaultwizard.new.title=Προσθήκη Νέας Κρύπτης
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Διαλέξτε ένα όνομα για την κρύπτη
|
||||
addvaultwizard.new.namePrompt=Όνομα Κρύπτης
|
||||
@@ -93,6 +91,7 @@ addvault.new.readme.accessLocation.2=Αυτή είναι η τοποθεσία
|
||||
addvault.new.readme.accessLocation.3=Κάθε αρχείο που θα προστεθεί σε αυτό τον τόμο θα κωδικοποιηθεί από το Cryptomator. Μπορείτε να το επεξεργαστείτε όπως θα κάνατε σε κάθε δίσκο/φάκελο. Αυτή είναι μόνο για αποκρυπτογραφημένη μορφή των περιεχομένων του, τα αρχεία σας μένουν κωδικοποιημένα στον σκληρό σας δίσκο συνέχεια.
|
||||
addvault.new.readme.accessLocation.4=Μπορείτε ελεύθερα να αφαιρέσετε το αρχείο.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Προσθήκη Υπάρχοντος Κρύπτης
|
||||
addvaultwizard.existing.instruction=Επιλέξτε το αρχείο "vault.cryptomator" της υπάρχοντος κρύπτης. Αν υπάρχει μόνο ένα αρχείο με όνομα "masterkey.cryptomator", επιλέξτε αυτό.
|
||||
addvaultwizard.existing.chooseBtn=Επιλογή…
|
||||
addvaultwizard.existing.filePickerTitle=Επιλέξτε Αρχείο Κρύπτης
|
||||
@@ -138,7 +137,7 @@ unlock.error.customPath.message=Αδυναμία προσάρτησης της
|
||||
unlock.error.customPath.description.notSupported=Εάν θέλετε να συνεχίσετε να χρησιμοποιείτε την προσαρμοσμένη διαδρομή, μεταβείτε στις προτιμήσεις και επιλέξτε έναν τύπο τόμου που την υποστηρίζει. Διαφορετικά, μεταβείτε στις επιλογές της κρύπτης και επιλέξτε ένα υποστηριζόμενο σημείο προσάρτησης.
|
||||
unlock.error.customPath.description.notExists=Η προσαρμοσμένη διαδρομή προσάρτησης δεν υπάρχει. Είτε δημιουργήστε την στο τοπικό σύστημα αρχείων σας είτε αλλάξτε την στις επιλογές κρύπτης.
|
||||
unlock.error.customPath.description.inUse=Το γράμμα μονάδας δίσκου ή προσαρμοσμένη διαδρομή προσάρτησης "%s" είναι ήδη σε χρήση.
|
||||
unlock.error.customPath.description.hideawayNotDir=Το προσωρινό, κρυφό αρχείο "%3$s" που χρησιμοποιείται για το ξεκλείδωμα δεν μπορεί να αφαιρεθεί. Παρακαλώ ελέγξτε το αρχείο και στη συνέχεια διαγράψτε το χειροκίνητα.
|
||||
unlock.error.customPath.description.hideawayNotDir=Το προσωρινό, κρυφό αρχείο "%3$s" που χρησιμοποιείται για το ξεκλείδωμα δεν μπορεί να αφαιρεθεί. Παρακαλούμε ελέγξτε το αρχείο και στη συνέχεια διαγράψτε το χειροκίνητα.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Η κρύπτη σας δεν μπορεί να τοποθετηθεί στη διαδρομή "%s". Παρακαλώ δοκιμάστε ξανά ή επιλέξτε διαφορετική διαδρομή.
|
||||
unlock.error.customPath.description.notEmptyDir=Η προσαρμοσμένη διαδρομή προσάρτησης "%s" δεν είναι ένας άδειος φάκελος. Παρακαλώ επιλέξτε έναν άδειο φάκελο και προσπαθήστε ξανά.
|
||||
unlock.error.customPath.description.generic=Έχετε επιλέξει μια προσαρμοσμένη διαδρομή προσάρτησης για αυτή την κρύπτη, αλλά η χρήση της απέτυχε με το μήνυμα: %2$s
|
||||
@@ -154,9 +153,10 @@ hub.auth.loginLink=Δεν έγινε ανακατεύθυνση; Κάντε κλ
|
||||
hub.receive.message=Επεξεργασία απάντησης…
|
||||
hub.receive.description=Το Cryptomator λαμβάνει και επεξεργάζεται την απάντηση από το Hub. Παρακαλώ περιμένετε.
|
||||
### Register Device
|
||||
hub.register.message=Απαιτείται όνομα συσκευής
|
||||
hub.register.description=Αυτή φαίνεται να είναι η πρώτη πρόσβαση στο Hub από αυτήν τη συσκευή. Για να την αναγνωρίσετε για εξουσιοδότηση πρόσβασης, πρέπει να ονομάσετε αυτήν τη συσκευή.
|
||||
hub.register.message=Νέα Συσκευή
|
||||
hub.register.description=Αυτή είναι η πρώτη πρόσβαση Hub από αυτήν τη συσκευή. Παρακαλούμε εξουσιοδοτήστε την χρησιμοποιώντας το κλειδί του λογαριασμού σας.
|
||||
hub.register.nameLabel=Όνομα Συσκευής
|
||||
hub.register.invalidAccountKeyLabel=Μη Έγκυρο Κλειδί Λογαριασμού
|
||||
hub.register.occupiedMsg=Το όνομα χρησιμοποιείται ήδη
|
||||
hub.register.registerBtn=Επιβεβαίωση
|
||||
### Registration Success
|
||||
@@ -168,6 +168,11 @@ hub.registerFailed.description=Παρουσιάστηκε σφάλμα στη δ
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Δεν επιτρέπεται η πρόσβαση
|
||||
hub.unauthorized.description=Η συσκευή σας δεν έχει ακόμη εξουσιοδοτηθεί να έχει πρόσβαση σε αυτή την κρύπτη. Ζητήστε από τον κάτοχο της κρύπτης να την εξουσιοδοτήσει.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Απαιτείται ενέργεια
|
||||
hub.requireAccountInit.description.0=Για να συνεχίσετε, παρακαλούμε ολοκληρώστε τα βήματα που απαιτούνται στο δικό σας
|
||||
hub.requireAccountInit.description.1=προφίλ χρήστη Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Μη έγκυρη Άδεια Hub
|
||||
hub.invalidLicense.description=Η συνεδρία σας στο Cryptomator Hub έχει μη έγκυρη άδεια χρήσης. Ενημερώστε έναν διαχειριστή του Hub για να αναβαθμίσει ή να ανανεώσει την άδεια χρήσης.
|
||||
@@ -366,7 +371,9 @@ main.vaultlist.contextMenu.unlock=Ξεκλείδωμα…
|
||||
main.vaultlist.contextMenu.unlockNow=Ξεκλείδωμα τώρα
|
||||
main.vaultlist.contextMenu.vaultoptions=Εμφάνιση επιλογών Vault
|
||||
main.vaultlist.contextMenu.reveal=Αποκάλυψη εικονικού δίσκου
|
||||
main.vaultlist.addVaultBtn=Προσθήκη Κρύπτης
|
||||
main.vaultlist.addVaultBtn=Προσθήκη
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Νέα Κρύπτη...
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Υπάρχουσα Κρύπτη...
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Ευχαριστούμε που επιλέξατε το Cryptomator για να προστατεύσετε τα αρχεία σας. Αν χρειάζεστε οποιαδήποτε βοήθεια, ελέγξτε τους οδηγούς για αρχάριους:
|
||||
|
||||
@@ -40,10 +40,8 @@ traymenu.vault.reveal=Revelar
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Añadir bóveda
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Crear bóveda nueva
|
||||
addvaultwizard.welcome.existingButton=Abrir bóveda existente
|
||||
## New
|
||||
addvaultwizard.new.title=Añadir nueva bóveda
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Elegir un nombre para la bóveda
|
||||
addvaultwizard.new.namePrompt=Nombre de la bóveda
|
||||
@@ -93,6 +91,7 @@ addvault.new.readme.accessLocation.2=Este es el lugar de acceso de la bóveda.
|
||||
addvault.new.readme.accessLocation.3=Los archivos agregados a este volumen serán cifrados por Cryptomator. Se puede trabajar con ellos como en cualquier otra unidad/carpeta. Esta es sólo una vista descifrada del contenido, sus archivos permanecen cifrados en el disco duro todo el tiempo.
|
||||
addvault.new.readme.accessLocation.4=No dude en eliminar este archivo.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Añadir bóveda existente
|
||||
addvaultwizard.existing.instruction=Elija el archivo "vault.cryptomator" de su bóveda existente. Si solo existe un archivo llamado "masterkey.cryptomator", selecciónelo en su lugar.
|
||||
addvaultwizard.existing.chooseBtn=Elegir…
|
||||
addvaultwizard.existing.filePickerTitle=Seleccionar archivo de bóveda
|
||||
@@ -154,9 +153,10 @@ hub.auth.loginLink=¿No se ha redireccionado? Haga clic aquí para abrirla.
|
||||
hub.receive.message=Procesando la respuesta…
|
||||
hub.receive.description=Cryptomator está recibiendo y procesando la respuesta del Hub. Por favor espere.
|
||||
### Register Device
|
||||
hub.register.message=Nombre del dispositivo requerido
|
||||
hub.register.description=Este parece ser el primer acceso al Hub desde este dispositivo. Para identificarlo y autorizar el acceso, necesita nombrar este dispositivo.
|
||||
hub.register.message=Nuevo dispositivo
|
||||
hub.register.description=Este es el primer acceso al Hub desde este dispositivo. Por favor, autorícelo usando su Account Key.
|
||||
hub.register.nameLabel=Nombre del dispositivo
|
||||
hub.register.invalidAccountKeyLabel=Account Key no válida
|
||||
hub.register.occupiedMsg=Nombre ya en uso
|
||||
hub.register.registerBtn=Confirmar
|
||||
### Registration Success
|
||||
@@ -168,6 +168,11 @@ hub.registerFailed.description=Ocurrió un error en el nombramiento. Para más d
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Acceso denegado
|
||||
hub.unauthorized.description=Su dispositivo aún no ha sido autorizado para acceder a esta bóveda. Pídale al propietario de la bóveda que lo autorice.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Acción requerida
|
||||
hub.requireAccountInit.description.0=Para continuar, por favor complete los pasos necesarios en su
|
||||
hub.requireAccountInit.description.1=Perfil de usuario del Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Licencia del Hub inválida
|
||||
hub.invalidLicense.description=Su instancia del Hub de Cryptomator tiene una licencia inválida. Informe a un administrador del Hub para actualizar o renovar la licencia.
|
||||
@@ -366,7 +371,9 @@ main.vaultlist.contextMenu.unlock=Desbloquear…
|
||||
main.vaultlist.contextMenu.unlockNow=Desbloquear ahora
|
||||
main.vaultlist.contextMenu.vaultoptions=Mostrar opciones de la bóveda
|
||||
main.vaultlist.contextMenu.reveal=Revelar unidad
|
||||
main.vaultlist.addVaultBtn=Añadir bóveda
|
||||
main.vaultlist.addVaultBtn=Añadir
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Nueva bóveda...
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Bóveda existente...
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Gracias por elegir Cryptomator para proteger sus archivos. En caso de necesitar ayuda, revise nuestras guías:
|
||||
|
||||
@@ -30,9 +30,6 @@ traymenu.vault.reveal=ظاهر
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=اضافه کردن گاوصندوق
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=ساخت گاوصندوق جدید
|
||||
addvaultwizard.welcome.existingButton=باز کردن گاوصندوق موجود
|
||||
## New
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=یک نام برای گاوصندوق انتخاب کنید
|
||||
@@ -68,6 +65,7 @@ unlock.unlockBtn=بازکردن قفل
|
||||
### Registration Success
|
||||
### Registration Failed
|
||||
### Unauthorized
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
|
||||
# Lock
|
||||
@@ -112,7 +110,6 @@ main.closeBtn.tooltip=ببند
|
||||
main.supporterCertificateMissing.tooltip=لطفا کمک مالی در نظر بگیرند
|
||||
## Vault List
|
||||
main.vaultlist.contextMenu.lock=قفل
|
||||
main.vaultlist.addVaultBtn=اضافه کردن گاوصندوق
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
### Locked
|
||||
|
||||
@@ -37,9 +37,6 @@ traymenu.vault.reveal=Paljasta
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Lisää Vault
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Luo Uusi Vault
|
||||
addvaultwizard.welcome.existingButton=Avaa Olemassaoleva Vault
|
||||
## New
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Anna uusi nimi Vaultille
|
||||
@@ -141,8 +138,6 @@ hub.auth.loginLink=Uudelleenohjaus epäonnistui? Avaa tästä manuaalisesti.
|
||||
hub.receive.message=Odotetaan vastausta…
|
||||
hub.receive.description=Cryptomator yhdistää Hub:iin. Ole hyvä ja odota.
|
||||
### Register Device
|
||||
hub.register.message=Laitteen nimi vaaditaan
|
||||
hub.register.description=Et ole ilmeisesti ennen yhdistänyt Hub:iin tältä laitteelta. Jotta pääsyoikeus voidaan todentaa, sinun täytyy nimetä tämä laite.
|
||||
hub.register.nameLabel=Laitteen Nimi
|
||||
hub.register.occupiedMsg=Tämä nimi on jo käytössä
|
||||
hub.register.registerBtn=Vahvista
|
||||
@@ -155,6 +150,7 @@ hub.registerFailed.description=Nimeämisprosessissa tapahtui virhe. Löydät lis
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Pääsy estetty
|
||||
hub.unauthorized.description=Laitteellasi ei ole pääsyvaltuutusta tähän holviin. Pyydä holvin omistajaa lisäämän valtuutus laitteellesi.
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Hub-lisenssi ei ole voimassa
|
||||
hub.invalidLicense.description=Cryptomator Hub:illasi ei ole voimassa olevaa lisenssiä. Ole hyvä ja ilmoita Hubin järjestelmänvalvojalle lisenssin päivittämiseksi tai sen uusimiseksi.
|
||||
@@ -233,7 +229,6 @@ main.preferencesBtn.tooltip=Asetukset
|
||||
main.vaultlist.contextMenu.lock=Lukitse
|
||||
main.vaultlist.contextMenu.unlockNow=Avaa Nyt
|
||||
main.vaultlist.contextMenu.reveal=Paljasta Asema
|
||||
main.vaultlist.addVaultBtn=Lisää Vault
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
### Locked
|
||||
|
||||
@@ -20,6 +20,10 @@ error.description=Oops! Hindi inaasahan ng Cryptomator na ito'y mangyari. Maaari
|
||||
error.hyperlink.lookup=Hanapin ang solusyon
|
||||
error.hyperlink.report=I-report ang problema
|
||||
error.technicalDetails=Mga detalye:
|
||||
error.existingSolutionDescription=Hindi inaasahan ng Cryptomator na mangyayari ito. Ngunit nakakita kami ng kasalukuyang solusyon para sa error na ito. Mangyaring tingnan ang sumusunod na link.
|
||||
error.hyperlink.solution=Hanapin ang solusyon
|
||||
error.lookupPermissionMessage=Maaaring maghanap ng solusyon ang Cryptomator para sa problemang ito online. Magpapadala ito ng kahilingan sa aming database ng problema mula sa iyong IP address.
|
||||
error.dismiss=I-dismiss
|
||||
error.lookUpSolution=Itignan ang solusyon
|
||||
|
||||
# Defaults
|
||||
@@ -36,10 +40,8 @@ traymenu.vault.reveal=Ipakita
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Magdagdag ng Vault
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Gumawa ng Bagong Vault
|
||||
addvaultwizard.welcome.existingButton=Magbukas ng Umiiral na Vault
|
||||
## New
|
||||
addvaultwizard.new.title=Magdagdag ng Bagong Vault
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Pangalanan ang vault
|
||||
addvaultwizard.new.namePrompt=Pangalan ng Vault
|
||||
@@ -50,130 +52,469 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Custom Location
|
||||
addvaultwizard.new.directoryPickerButton=Mamili…
|
||||
addvaultwizard.new.directoryPickerTitle=Pumili ng Direktoryo
|
||||
addvaultwizard.new.fileAlreadyExists=Mayroon nang file o direktoryo na may pangalan ng vault
|
||||
addvaultwizard.new.locationDoesNotExist=Ang isang direktoryo sa tinukoy na landas ay hindi umiiral o hindi ma-access
|
||||
addvaultwizard.new.locationIsNotWritable=Walang access sa pagsulat sa tinukoy na landas
|
||||
addvaultwizard.new.locationIsOk=Angkop na lokasyon para sa iyong vault
|
||||
addvaultwizard.new.invalidName=Di-wastong pangalan ng vault
|
||||
addvaultwizard.new.validName=Wastong pangalan ng vault
|
||||
addvaultwizard.new.validCharacters.message=Maaaring naglalaman ang pangalan ng vault ng mga sumusunod na character:
|
||||
addvaultwizard.new.validCharacters.chars=Mga character ng salita (hal. a, ж o 수)
|
||||
addvaultwizard.new.validCharacters.numbers=Numero
|
||||
addvaultwizard.new.validCharacters.dashes=Hyphen (%s) o underscore (%s)
|
||||
### Expert Settings
|
||||
addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=Paganahin ang mga setting ng eksperto
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.invalid=Maglagay ng value sa pagitan ng 36 at 220 (default 220)
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=Ibukas ang dokumentasyon para matuto pa.
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.title=Pinakamataas na haba ng mga naka-encrypt na pangalan ng file
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.valid=Wasto
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Gumawa ng bagong Vault
|
||||
addvaultwizard.new.generateRecoveryKeyChoice=Hindi mo maa-access ang iyong data nang wala ang iyong password. Gusto mo ba ng recovery key para sa kaso na nawala mo ang iyong password?
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Oo pakiusap, mas ligtas kaysa sorry
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Hindi, salamat, hindi ko mawawala ang aking password
|
||||
### Information
|
||||
addvault.new.readme.storageLocation.fileName=MAHALAGA.rtf
|
||||
addvault.new.readme.storageLocation.1=⚠️ VAULT FILES ⚠️
|
||||
addvault.new.readme.storageLocation.2=Ito ang lokasyon ng iyong vault na imbakan.
|
||||
addvault.new.readme.storageLocation.3=HUWAG
|
||||
addvault.new.readme.storageLocation.4=• baguhin ang anumang mga file sa loob ng direktoryong ito o
|
||||
addvault.new.readme.storageLocation.5=• i-paste ang anumang mga file para sa pag-encrypt sa direktoryong ito.
|
||||
addvault.new.readme.storageLocation.6=Kung gusto mo i-encrypt ang mga files at makita nang nilalaman ng vault, gawin ang nakasabi:
|
||||
addvault.new.readme.storageLocation.7=1. Idagdag ang vault na ito sa Cryptomator.
|
||||
addvault.new.readme.storageLocation.8=2. I-unlock ang vault sa Cryptomator.
|
||||
addvault.new.readme.storageLocation.9=3. Buksan ang lokasyon ng pag-access sa pamamagitan ng pag-click sa pindutang "Ibunyag".
|
||||
addvault.new.readme.storageLocation.10=Kung kailangan mo ng tulong, bisitahin ang dokumentasyon: %s
|
||||
addvault.new.readme.accessLocation.fileName=WELCOME.rtf
|
||||
addvault.new.readme.accessLocation.1=🔐️ Naka-encrypt na VOLUME 🔐️
|
||||
addvault.new.readme.accessLocation.2=Ito ang lokasyon ng iyong vault.
|
||||
addvault.new.readme.accessLocation.3=Ang anumang mga file na idinagdag sa volume na ito ay ie-encrypt ng Cryptomator. Maaari mong gawin ito tulad ng sa anumang iba pang drive/folder. Ito ay isang decrypted view lamang ng nilalaman nito, ang iyong mga file ay mananatiling naka-encrypt sa iyong hard drive sa lahat ng oras.
|
||||
addvault.new.readme.accessLocation.4=Huwag mag-atubiling tanggalin ang file na ito.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Magdagdag ng Umiiral na Vault
|
||||
addvaultwizard.existing.instruction=Piliin ang "vault.cryptomator" file ng iyong kasalukuyang vault. Kung mayroon lamang file na may pangalang "masterkey.cryptomator," piliin iyon sa halip.
|
||||
addvaultwizard.existing.chooseBtn=Mamili…
|
||||
addvaultwizard.existing.filePickerTitle=Piliin ang Vault File
|
||||
addvaultwizard.existing.filePickerMimeDesc=Cryptomator Vault
|
||||
## Success
|
||||
addvaultwizard.success.nextStepsInstructions=Idinagdag ang vault na "%s".\nKailangan mong i-unlock ang vault na ito para ma-access o magdagdag ng mga content. Bilang kahalili, maaari mo itong i-unlock sa anumang susunod na oras.
|
||||
addvaultwizard.success.unlockNow=I-unlock Ngayon
|
||||
|
||||
# Remove Vault
|
||||
removeVault.title=Tanggalin %s
|
||||
removeVault.message=Itangal ang vault?
|
||||
removeVault.description=Makakalimutan lang nito ang Cryptomator tungkol sa vault na ito. Maaari mo itong idagdag muli. Walang matatanggal na mga naka-encrypt na file mula sa iyong hard drive.
|
||||
removeVault.confirmBtn=Itangal ang vault
|
||||
|
||||
# Change Password
|
||||
changepassword.title=Palitan ANG password
|
||||
changepassword.enterOldPassword=Ilagay ang kasalukuyang password para sa "%s"
|
||||
changepassword.finalConfirmation=Naiintindihan ko na hindi ko maa-access ang aking data kung nakalimutan ko ang aking password
|
||||
|
||||
# Forget Password
|
||||
forgetPassword.title=Kalimutan ang Password
|
||||
forgetPassword.message=Nakalimutan ang iyong password?
|
||||
forgetPassword.description=Tatanggalin nito ang naka-save na password ng vault na ito mula sa keychain ng iyong system.
|
||||
forgetPassword.confirmBtn=Kalimutan ang Password
|
||||
|
||||
# Unlock
|
||||
unlock.title=I-unlock ang "%s"
|
||||
unlock.passwordPrompt=Ipasok ang password para sa "%s":
|
||||
unlock.savePassword=Maalala ang password
|
||||
unlock.unlockBtn=I-unlock
|
||||
## Select
|
||||
unlock.chooseMasterkey.message=Hindi nahanap ang masterkey file
|
||||
unlock.chooseMasterkey.description=Hindi mahanap ng Cryptomator ang masterkey file para sa vault na "%s". Mangyaring piliin ang key file nang manu-mano.
|
||||
unlock.chooseMasterkey.filePickerTitle=Piliin ang masterkey file
|
||||
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator Masterkey
|
||||
## Success
|
||||
unlock.success.message=Matagumpay ang pag-unlock
|
||||
unlock.success.description=Ang nilalaman sa vault na "%s" ay naa-access na ngayon sa mount point nito.
|
||||
unlock.success.rememberChoice=Paalala ang pinili ko, huwag mag tanong ulit
|
||||
unlock.success.revealBtn=Ibunyag ang Drive
|
||||
## Failure
|
||||
unlock.error.customPath.message=Hindi ma-mount ang vault sa custom na path
|
||||
unlock.error.customPath.description.notSupported=Kung gusto mong patuloy na gamitin ang custom na path, mangyaring pumunta sa mga kagustuhan at pumili ng uri ng volume na sumusuporta dito. Kung hindi, pumunta sa mga opsyon sa vault at pumili ng sinusuportahang mount point.
|
||||
unlock.error.customPath.description.notExists=Ang custom na mount path ay hindi umiiral. Alinman sa lumikha ito sa iyong lokal na filesystem o baguhin ito sa mga pagpipilian sa vault.
|
||||
unlock.error.customPath.description.inUse=Ang drive letter o custom na mount path na "%s" ay ginagamit na.
|
||||
unlock.error.customPath.description.hideawayNotDir=Ang pansamantalang nakatagong file na "%3$s" na ginamit para sa pag-unlock ay hindi maalis. Pakisuri ang file at pagkatapos ay tanggalin ito nang manu-mano.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Hindi ma-mount ang iyong vault sa path na "%s". Pakisubukang muli o pumili ng ibang landas.
|
||||
unlock.error.customPath.description.notEmptyDir=Ang custom na mount path na "%s" ay hindi isang walang laman na folder. Mangyaring pumili ng isang walang laman na folder at subukang muli.
|
||||
unlock.error.customPath.description.generic=Pumili ka ng custom na mount path para sa vault na ito, ngunit nabigo ang paggamit nito sa mensaheng: %2$s
|
||||
## Hub
|
||||
hub.noKeychain.message=Hindi ma-access ang key ng device
|
||||
hub.noKeychain.description=Para ma-unlock ang mga Hub vault, kailangan ng device key, na sini-secure gamit ang keychain. Upang magpatuloy, paganahin ang “%s” at pumili ng keychain sa mga kagustuhan.
|
||||
hub.noKeychain.openBtn=Buksan ang Mga Kagustuhan
|
||||
### Waiting
|
||||
hub.auth.message=Naghihintay para sa pagpapatunay…
|
||||
hub.auth.description=Dapat kang awtomatikong mai-redirect sa pahina ng pag-login.
|
||||
hub.auth.loginLink=Hindi na-redirect? Mag-click dito upang buksan ito.
|
||||
### Receive Key
|
||||
hub.receive.message=Pinoproseso ang tugon…
|
||||
hub.receive.description=Ang Cryptomator ay tumatanggap at nagpoproseso ng tugon mula sa Hub. Mangyaring maghintay.
|
||||
### Register Device
|
||||
hub.register.message=Bagong Device
|
||||
hub.register.description=Ito ang unang Hub access mula sa device na ito. Mangyaring pahintulutan ito gamit ang iyong Account Key.
|
||||
hub.register.nameLabel=Pangalan ng device
|
||||
hub.register.invalidAccountKeyLabel=Di-wastong Account Key
|
||||
hub.register.occupiedMsg=Ang pangalan ay nagamit na
|
||||
hub.register.registerBtn=Kumpirmahin
|
||||
### Registration Success
|
||||
hub.registerSuccess.message=Pinangalanan ang device
|
||||
hub.registerSuccess.description=Para ma-access ang vault, kailangang pahintulutan ng may-ari ng vault ang iyong device.
|
||||
### Registration Failed
|
||||
hub.registerFailed.message=Nabigo ang pagpapangalan ng device
|
||||
hub.registerFailed.description=Nagkaroon ng error sa proseso ng pagbibigay ng pangalan. Para sa higit pang mga detalye, tingnan ang log ng aplikasyon.
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Walang pahintulot
|
||||
hub.unauthorized.description=Hindi pa pinahihintulutan ang iyong device na i-access ang vault na ito. Hilingin sa may-ari ng vault na pahintulutan ito.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Kinakailangan ang pagkilos
|
||||
hub.requireAccountInit.description.0=Upang magpatuloy, mangyaring kumpletuhin ang mga hakbang na kinakailangan sa iyong
|
||||
hub.requireAccountInit.description.1=Profile ng user ng hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Di-wasto ang Lisensya ng Hub
|
||||
hub.invalidLicense.description=Ang iyong Cryptomator Hub instance ay may di-wastong lisensya. Mangyaring ipagbigay-alam sa administrator ng Hub na mag-upgrade o mag-renew ng lisensya.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
lock.forced.message=Nabigo ang pag-lock
|
||||
lock.forced.description=Ang pag-lock ng "%s" ay na-block ng mga nakabinbing operasyon o mga bukas na file. Maaari mong pilitin na i-lock ang vault na ito, gayunpaman ang pagkagambala sa I/O ay maaaring magresulta sa pagkawala ng hindi na-save na data.
|
||||
lock.forced.retryBtn=Subukan muli
|
||||
lock.forced.forceBtn=Force Lock
|
||||
## Failure
|
||||
lock.fail.message=Nabigo ang pag-lock ng vault
|
||||
lock.fail.description=Hindi ma-lock ang Vault "%s". Tiyaking nai-save ang hindi na-save na gawain sa ibang lugar at natapos ang mahahalagang operasyon ng Read/Write. Upang isara ang vault, patayin ang proseso ng Cryptomator.
|
||||
|
||||
# Migration
|
||||
migration.title=I-upgrade ang Vault
|
||||
## Start
|
||||
migration.start.header=I-upgrade ang Vault
|
||||
migration.start.text=Upang mabuksan ang iyong vault na "%s" sa bagong bersyon na ito ng Cryptomator, kailangang i-upgrade ang vault sa mas bagong format. Bago gawin ito, dapat mong malaman ang mga sumusunod:
|
||||
migration.start.remarkUndone=Hindi maa-undo ang pag-upgrade na ito.
|
||||
migration.start.remarkVersions=Hindi mabubuksan ng mga lumang bersyon ng Cryptomator ang na-upgrade na vault.
|
||||
migration.start.remarkCanRun=Dapat mong tiyakin na ang bawat device kung saan mo ina-access ang vault ay maaaring magpatakbo ng bersyong ito ng Cryptomator.
|
||||
migration.start.remarkSynced=Dapat mong tiyakin na ang iyong vault ay ganap na naka-sync sa device na ito, at sa iyong iba pang mga device, bago ito i-upgrade.
|
||||
migration.start.confirm=Nabasa at naunawaan ko ang impormasyon sa itaas
|
||||
## Run
|
||||
migration.run.enterPassword=Ilagay ang password para sa "%s"
|
||||
migration.run.startMigrationBtn=I-migrate ang Vault
|
||||
migration.run.progressHint=Maaaring tumagal ito ng ilang oras…
|
||||
## Success
|
||||
migration.success.nextStepsInstructions=Matagumpay na nailipat ang "%s".\nMaaari mo na ngayong i-unlock ang iyong vault.
|
||||
migration.success.unlockNow=I-unlock Ngayon
|
||||
## Missing file system capabilities
|
||||
migration.error.missingFileSystemCapabilities.title=Hindi sinusuportahang File System
|
||||
migration.error.missingFileSystemCapabilities.description=Hindi nasimulan ang paglipat, dahil ang iyong vault ay matatagpuan sa isang hindi sapat na file system.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Hindi sinusuportahan ng file system ang mahabang pangalan ng file.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Hindi sinusuportahan ng file system ang mahahabang landas.
|
||||
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Hindi pinapayagan ng file system na basahin.
|
||||
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Ang file system ay hindi pinapayagang isulat sa.
|
||||
## Impossible
|
||||
migration.impossible.heading=Hindi ma-migrate ang vault
|
||||
migration.impossible.reason=Hindi maaaring awtomatikong i-migrate ang vault dahil hindi tugma ang lokasyon ng storage o access point nito.
|
||||
migration.impossible.moreInfo=Mabubuksan pa rin ang vault gamit ang mas lumang bersyon. Para sa mga tagubilin kung paano manu-manong mag-migrate ng vault, bisitahin ang
|
||||
|
||||
# Health Check
|
||||
## Start
|
||||
health.title=Health Check ng "%s"
|
||||
health.intro.header=Health Check
|
||||
health.intro.text=Ang Health Check ay isang koleksyon ng mga pagsusuri upang makita at posibleng ayusin ang mga problema sa panloob na istraktura ng iyong vault. Mangyaring tandaan:
|
||||
health.intro.remarkSync=Tiyaking ganap na naka-sync ang lahat ng device, nalulutas nito ang karamihan sa mga problema.
|
||||
health.intro.remarkFix=Hindi lahat ng problema ay kayang ayusin.
|
||||
health.intro.remarkBackup=Kung nasira ang data, isang backup lamang ang makakatulong.
|
||||
health.intro.affirmation=Nabasa at naunawaan ko ang impormasyon sa itaas
|
||||
## Start Failure
|
||||
health.fail.header=Error sa pag-load ng Vault Configuration
|
||||
health.fail.ioError=Nagkaroon ng error habang ina-access at binabasa ang config file.
|
||||
health.fail.parseError=Nagkaroon ng error habang pina-parse ang config ng vault.
|
||||
health.fail.moreInfo=Karagdagang impormasyon
|
||||
## Check Selection
|
||||
health.checkList.description=Pumili ng mga tseke sa kaliwang listahan o gamitin ang mga button sa ibaba.
|
||||
health.checkList.selectAllButton=Piliin ang Lahat ng Pagsusuri
|
||||
health.checkList.deselectAllButton=Alisin sa pagkakapili ang Lahat ng Pagsusuri
|
||||
health.check.runBatchBtn=Patakbuhin ang Mga Napiling Pagsusuri
|
||||
## Detail view
|
||||
health.check.detail.noSelectedCheck=Para sa mga resulta pumili ng natapos na pagsusuri sa kalusugan sa kaliwang listahan.
|
||||
health.check.detail.checkScheduled=The check is scheduled.
|
||||
health.check.detail.checkRunning=The check is currently running…
|
||||
health.check.detail.checkSkipped=Ang tseke ay hindi napiling tumakbo.
|
||||
health.check.detail.checkFinished=Matagumpay na natapos ang tseke.
|
||||
health.check.detail.checkFinishedAndFound=Ang tseke ay tapos nang tumakbo. Mangyaring suriin ang mga resulta.
|
||||
health.check.detail.checkFailed=Ang tseke ay lumabas dahil sa isang error.
|
||||
health.check.detail.checkCancelled=Kinansela ang tseke.
|
||||
health.check.detail.listFilters.label=Filter
|
||||
health.check.detail.fixAllSpecificBtn=Ayusin ang lahat ng uri
|
||||
health.check.exportBtn=I-export ang Ulat
|
||||
## Result view
|
||||
health.result.severityFilter.all=Kalubhaan - Lahat
|
||||
health.result.severityFilter.good=Mabuti
|
||||
health.result.severityFilter.info=Impormasyon
|
||||
health.result.severityFilter.warn=Babala
|
||||
health.result.severityFilter.crit=Mapanganib
|
||||
health.result.severityTip.good=Seryoso: Mabuti\nNormal na istraktura ng vault.
|
||||
health.result.severityTip.info=Kalubhaan: Impormasyon\nBuo ang istraktura ng Vault, iminungkahing ayusin.
|
||||
health.result.severityTip.warn=Kalubhaan: Babala\nNasira ang istraktura ng Vault, lubos na pinapayuhan ang pag-aayos.
|
||||
health.result.severityTip.crit=Kalubhaan: Kritikal\nNasira ang istraktura ng Vault, natukoy ang pagkawala ng data.
|
||||
health.result.fixStateFilter.all=Ayusin ang estado - Lahat
|
||||
health.result.fixStateFilter.fixable=Naaayos
|
||||
health.result.fixStateFilter.notFixable=Hindi naaayos
|
||||
health.result.fixStateFilter.fixing=Inaayos…
|
||||
health.result.fixStateFilter.fixed=Nakapirming
|
||||
health.result.fixStateFilter.fixFailed=Nabigo ang pag-aayos
|
||||
## Fix Application
|
||||
health.fix.fixBtn=Ayusin
|
||||
health.fix.successTip=Ayusin ang matagumpay
|
||||
health.fix.failTip=Nabigo ang pag-aayos, tingnan ang log para sa mga detalye
|
||||
|
||||
# Preferences
|
||||
preferences.title=Mga Kagustuhan
|
||||
## General
|
||||
preferences.general=Heneral
|
||||
preferences.general.startHidden=Itago ang window kapag sinimulan ang Cryptomator
|
||||
preferences.general.autoCloseVaults=Awtomatikong i-lock ang mga bukas na vault kapag huminto sa aplikasyon
|
||||
preferences.general.debugLogging=Paganahin ang pag-log ng debug
|
||||
preferences.general.debugDirectory=Magbunyag ng mga log file
|
||||
preferences.general.autoStart=Ilunsad ang Cryptomator sa pagsisimula ng system
|
||||
preferences.general.keychainBackend=Mag-imbak ng mga password gamit ang
|
||||
## Interface
|
||||
preferences.interface=Interface
|
||||
preferences.interface.theme=Tingnan at Pakiramdam
|
||||
preferences.interface.theme.automatic=Awtomatiko
|
||||
preferences.interface.theme.dark=Dark
|
||||
preferences.interface.theme.light=Light
|
||||
preferences.interface.unlockThemes=I-unlock ang dark mode
|
||||
preferences.interface.language=Wika (nangangailangan ng pag-restart)
|
||||
preferences.interface.language.auto=System Default
|
||||
preferences.interface.interfaceOrientation=Oryentasyon ng Interface
|
||||
preferences.interface.interfaceOrientation.ltr=Kaliwa hanggang Kanan
|
||||
preferences.interface.interfaceOrientation.rtl=Kanan papuntang Kaliwa
|
||||
preferences.interface.showMinimizeButton=Ipakita ang pindutan ng minimize
|
||||
preferences.interface.showTrayIcon=Ipakita ang icon ng tray (kailangan i-restart)
|
||||
## Volume
|
||||
preferences.volume=Virtual Drive
|
||||
preferences.volume.type=Uri ng Dami
|
||||
preferences.volume.type.automatic=Awtomatiko
|
||||
preferences.volume.docsTooltip=Buksan ang dokumentasyon para matuto pa tungkol sa iba't ibang uri ng volume.
|
||||
preferences.volume.fuseRestartRequired=Upang mailapat ang mga pagbabago, kailangang i-restart ang Cryptomator.
|
||||
preferences.volume.tcp.port=TCP Port
|
||||
preferences.volume.supportedFeatures=Sinusuportahan ng napiling uri ng volume ang mga sumusunod na tampok:
|
||||
preferences.volume.feature.mountAuto=Awtomatikong pagpili ng mount point
|
||||
preferences.volume.feature.mountToDir=Custom na direktoryo bilang mount point
|
||||
preferences.volume.feature.mountToDriveLetter=Drive letter bilang mount point
|
||||
preferences.volume.feature.mountFlags=Mga pagpipilian sa custom na pag-mount
|
||||
preferences.volume.feature.readOnly=Read-only mount
|
||||
## Updates
|
||||
preferences.updates=Mga update
|
||||
preferences.updates.currentVersion=Kasalukuyang Bersyon: %s
|
||||
preferences.updates.autoUpdateCheck=Awtomatikong suriin ang mga update
|
||||
preferences.updates.checkNowBtn=Tingnan ngayon
|
||||
preferences.updates.updateAvailable=Available ang update sa bersyong %s.
|
||||
## Contribution
|
||||
preferences.contribute=Suportahan Kami
|
||||
preferences.contribute.registeredFor=Nakarehistro ang sertipiko ng tagasuporta para sa %s
|
||||
preferences.contribute.noCertificate=Suportahan ang Cryptomator at tumanggap ng sertipiko ng tagasuporta. Ito ay tulad ng isang susi ng lisensya ngunit para sa mga kahanga-hangang tao na gumagamit ng libreng software. ;-)
|
||||
preferences.contribute.getCertificate=Wala ka na ba? Alamin kung paano mo ito makukuha.
|
||||
preferences.contribute.promptText=I-paste ang code ng certificate ng tagasuporta dito
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Tungkol sa
|
||||
|
||||
# Vault Statistics
|
||||
stats.title=Mga istatistika para sa %s
|
||||
stats.cacheHitRate=Rate ng Cache Hit
|
||||
## Read
|
||||
stats.read.throughput.idle=Basahin: walang ginagawa
|
||||
stats.read.throughput.kibs=Read: %.2f KiB/s
|
||||
stats.read.throughput.mibs=Basahin: %.2f MiB/s
|
||||
stats.read.total.data.none=Nabasa ang data: -
|
||||
stats.read.total.data.kib=Nabasa ang data: %.1f KiB
|
||||
stats.read.total.data.mib=Nabasa ang data: %.1f MiB
|
||||
stats.read.total.data.gib=Nabasa ang data: %.1f GiB
|
||||
stats.decr.total.data.none=Na-decrypt ang data: -
|
||||
stats.decr.total.data.kib=Na-decrypt ang data: %.1f KiB
|
||||
stats.decr.total.data.mib=Na-decrypt ang data: %.1f MiB
|
||||
stats.decr.total.data.gib=Na-decrypt ang data: %.1f GiB
|
||||
stats.read.accessCount=Kabuuang nabasa: %d
|
||||
## Write
|
||||
stats.write.throughput.idle=Sumulat: walang ginagawa
|
||||
stats.write.throughput.kibs=Sumulat: %.2f KiB/s
|
||||
stats.write.throughput.mibs=Sumulat: %.2f MiB/s
|
||||
stats.write.total.data.none=Nakasulat na datos: -
|
||||
stats.write.total.data.kib=Nakasulat na datos: %.1f KiB
|
||||
stats.write.total.data.mib=Data na nakasulat: %.1f MiB
|
||||
stats.write.total.data.gib=Nakasulat na datos: %.1f GiB
|
||||
stats.encr.total.data.none=Naka-encrypt na data: -
|
||||
stats.encr.total.data.kib=Naka-encrypt na data: %.1f KiB
|
||||
stats.encr.total.data.mib=Naka-encrypt na data: %.1f MiB
|
||||
stats.encr.total.data.gib=Naka-encrypt na data: %.1f GiB
|
||||
stats.write.accessCount=Kabuuang pagsusulat: %d
|
||||
|
||||
## Accesses
|
||||
stats.access.current=Access: %d
|
||||
stats.access.total=Kabuuang mga access: %d
|
||||
|
||||
|
||||
# Main Window
|
||||
main.closeBtn.tooltip=Isara
|
||||
main.minimizeBtn.tooltip=I-minimize
|
||||
main.preferencesBtn.tooltip=Mga Kagustuhan
|
||||
main.debugModeEnabled.tooltip=Naka-enable ang debug mode
|
||||
main.supporterCertificateMissing.tooltip=Mangyaring isaalang-alang ang pagbibigay ng donasyon
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Mag-click dito para magdagdag ng vault
|
||||
main.vaultlist.contextMenu.remove=Alisin…
|
||||
main.vaultlist.contextMenu.lock=I-lock
|
||||
main.vaultlist.addVaultBtn=Magdagdag ng Vault
|
||||
main.vaultlist.contextMenu.unlock=I-unlock…
|
||||
main.vaultlist.contextMenu.unlockNow=I-unlock Ngayon
|
||||
main.vaultlist.contextMenu.vaultoptions=Ipakita ang Mga Opsyon sa Vault
|
||||
main.vaultlist.contextMenu.reveal=Ibunyag ang Drive
|
||||
main.vaultlist.addVaultBtn=Idagdag
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Bagong Vault...
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Kasalukuyang Vault...
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Salamat sa pagpili sa Cryptomator para protektahan ang iyong mga file. Kung kailangan mo ng anumang tulong, tingnan ang aming mga gabay sa pagsisimula:
|
||||
### Locked
|
||||
main.vaultDetail.lockedStatus=NAKA-LOCK
|
||||
main.vaultDetail.unlockBtn=I-unlock…
|
||||
main.vaultDetail.unlockNowBtn=I-unlock Ngayon
|
||||
main.vaultDetail.optionsBtn=Mga Pagpipilian sa Vault
|
||||
main.vaultDetail.passwordSavedInKeychain=Na-save ang password
|
||||
### Unlocked
|
||||
main.vaultDetail.unlockedStatus=NAKA-unlock
|
||||
main.vaultDetail.accessLocation=Ang mga nilalaman ng iyong vault ay maa-access dito:
|
||||
main.vaultDetail.revealBtn=Ibunyag ang Drive
|
||||
main.vaultDetail.copyUri=Kopyahin ang URI
|
||||
main.vaultDetail.lockBtn=I-lock
|
||||
main.vaultDetail.bytesPerSecondRead=Basahin:
|
||||
main.vaultDetail.bytesPerSecondWritten=Sumulat:
|
||||
main.vaultDetail.throughput.idle=walang ginagawa
|
||||
main.vaultDetail.throughput.kbps=%.1f KiB/s
|
||||
main.vaultDetail.throughput.mbps=%.1f MiB/s
|
||||
main.vaultDetail.stats=Mga Istatistika ng Vault
|
||||
main.vaultDetail.locateEncryptedFileBtn=Hanapin ang Naka-encrypt na File
|
||||
main.vaultDetail.locateEncryptedFileBtn.tooltip=Pumili ng file mula sa iyong vault upang mahanap ang naka-encrypt na katapat nito
|
||||
main.vaultDetail.encryptedPathsCopied=Mga Path na Nakopya sa Clipboard!
|
||||
main.vaultDetail.filePickerTitle=Piliin ang File Inside Vault
|
||||
### Missing
|
||||
main.vaultDetail.missing.info=Hindi makahanap ng vault ang Cryptomator sa landas na ito.
|
||||
main.vaultDetail.missing.recheck=Suriin muli
|
||||
main.vaultDetail.missing.remove=Alisin sa Listahan ng Vault…
|
||||
main.vaultDetail.missing.changeLocation=Baguhin ang Lokasyon ng Vault…
|
||||
### Needs Migration
|
||||
main.vaultDetail.migrateButton=I-upgrade ang Vault
|
||||
main.vaultDetail.migratePrompt=Kailangang i-upgrade ang iyong vault sa bagong format, bago mo ito ma-access
|
||||
### Error
|
||||
main.vaultDetail.error.info=Nagkaroon ng error sa paglo-load ng vault mula sa disk.
|
||||
main.vaultDetail.error.reload=Reload
|
||||
main.vaultDetail.error.windowTitle=Error sa paglo-load ng vault
|
||||
|
||||
# Wrong File Alert
|
||||
wrongFileAlert.title=Paano Mag-encrypt ng mga File
|
||||
wrongFileAlert.message=Sinubukan mo bang i-encrypt ang mga file na ito?
|
||||
wrongFileAlert.description=Para sa layuning ito, nagbibigay ang Cryptomator ng volume sa iyong system file manager.
|
||||
wrongFileAlert.instruction.0=Upang i-encrypt ang mga file, sundin ang mga hakbang na ito:
|
||||
wrongFileAlert.instruction.1=1. I-unlock ang iyong vault.
|
||||
wrongFileAlert.instruction.2=2. Mag-click sa "Reveal" para buksan ang volume sa iyong file manager.
|
||||
wrongFileAlert.instruction.3=3. Idagdag ang iyong mga file sa volume na ito.
|
||||
wrongFileAlert.link=Para sa karagdagang tulong, bisitahin ang
|
||||
|
||||
# Vault Options
|
||||
## General
|
||||
vaultOptions.general=Heneral
|
||||
vaultOptions.general.vaultName=Pangalan ng Vault
|
||||
vaultOptions.general.autoLock.lockAfterTimePart1=I-lock kapag idle para sa
|
||||
vaultOptions.general.autoLock.lockAfterTimePart2=minuto
|
||||
vaultOptions.general.unlockAfterStartup=I-unlock ang vault kapag sinimulan ang Cryptomator
|
||||
vaultOptions.general.actionAfterUnlock=Pagkatapos ng matagumpay na pag-unlock
|
||||
vaultOptions.general.actionAfterUnlock.ignore=walang gawin
|
||||
vaultOptions.general.actionAfterUnlock.reveal=Ibunyag ang Drive
|
||||
vaultOptions.general.actionAfterUnlock.ask=Magtanong
|
||||
vaultOptions.general.startHealthCheckBtn=Simulan ang Health Check
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Pag-mount
|
||||
vaultOptions.mount.info=Ang mga opsyon ay depende sa napiling uri ng volume.
|
||||
vaultOptions.mount.linkToPreferences=Buksan ang mga kagustuhan sa virtual drive
|
||||
vaultOptions.mount.readonly=Basahin lamang
|
||||
vaultOptions.mount.customMountFlags=Mga custom na naka-mount na flag
|
||||
vaultOptions.mount.winDriveLetterOccupied=inookupahan
|
||||
vaultOptions.mount.mountPoint=Mount point
|
||||
vaultOptions.mount.mountPoint.auto=Awtomatikong pumili ng angkop na lokasyon
|
||||
vaultOptions.mount.mountPoint.driveLetter=Gumamit ng nakatalagang drive letter
|
||||
vaultOptions.mount.mountPoint.custom=Gamitin ang napiling direktoryo
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Mamili…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Pumili ng isang direktoryo
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Password
|
||||
vaultOptions.masterkey.changePasswordBtn=Palitan ANG password
|
||||
vaultOptions.masterkey.forgetSavedPasswordBtn=Kalimutan ang Naka-save na Password
|
||||
vaultOptions.masterkey.recoveryKeyExplanation=Ang recovery key ay ang tanging paraan mo upang maibalik ang access sa isang vault kung mawala mo ang iyong password.
|
||||
vaultOptions.masterkey.showRecoveryKeyBtn=Display Recovery Key
|
||||
vaultOptions.masterkey.recoverPasswordBtn=I-reset ang Password
|
||||
## Hub
|
||||
vaultOptions.hub=Pagbawi
|
||||
vaultOptions.hub.convertInfo=Maaari mong gamitin ang recovery key upang i-convert ang Hub vault na ito sa isang password-based na vault sa isang emergency.
|
||||
vaultOptions.hub.convertBtn=I-convert sa Password-Based Vault
|
||||
|
||||
# Recovery Key
|
||||
## Display Recovery Key
|
||||
recoveryKey.display.title=Ipakita ang Recovery Key
|
||||
recoveryKey.create.message=Kailangan ng password
|
||||
recoveryKey.create.description=Ilagay ang password para sa "%s" upang ipakita ang recovery key nito.
|
||||
recoveryKey.display.description=Ang sumusunod na recovery key ay maaaring gamitin upang ibalik ang access sa "%s":
|
||||
recoveryKey.display.StorageHints=Itago ito sa isang lugar na napaka-secure, hal.:\n • I-store ito gamit ang isang password manager\n • I-save ito sa isang USB flash drive\n • I-print ito sa papel
|
||||
## Reset Password
|
||||
### Enter Recovery Key
|
||||
recoveryKey.recover.title=I-reset ang Password
|
||||
recoveryKey.recover.prompt=Ilagay ang recovery key para sa "%s":
|
||||
recoveryKey.recover.correctKey=Tama ang recovery key na ito
|
||||
recoveryKey.recover.wrongKey=Ang recovery key na ito ay kabilang sa ibang vault
|
||||
recoveryKey.recover.invalidKey=Hindi wasto ang recovery key na ito
|
||||
recoveryKey.printout.heading=Cryptomator Recovery Key\n"%s"\n
|
||||
### Reset Password
|
||||
recoveryKey.recover.resetBtn=I-reset
|
||||
### Recovery Key Password Reset Success
|
||||
recoveryKey.recover.resetSuccess.message=Matagumpay ang pag-reset ng password
|
||||
recoveryKey.recover.resetSuccess.description=Maaari mong i-unlock ang iyong vault gamit ang bagong password.
|
||||
|
||||
# Convert Vault
|
||||
convertVault.title=I-convert ang Vault
|
||||
convertVault.convert.convertBtn.before=Magbalik-loob
|
||||
convertVault.convert.convertBtn.processing=Kino-convert…
|
||||
convertVault.success.message=Matagumpay ang conversion
|
||||
convertVault.hubToPassword.success.description=Maaari mo na ngayong i-unlock ang vault gamit ang napiling password nang hindi nangangailangan ng access sa Hub.
|
||||
|
||||
# New Password
|
||||
newPassword.promptText=Maglagay ng bagong password
|
||||
newPassword.reenterPassword=Kumpirmahin ang bagong password
|
||||
newPassword.passwordsMatch=Tugma ang mga password!
|
||||
newPassword.passwordsDoNotMatch=Hindi tugma ang mga password
|
||||
passwordStrength.messageLabel.tooShort=Gumamit ng hindi bababa sa %d character
|
||||
passwordStrength.messageLabel.0=Napakahina
|
||||
passwordStrength.messageLabel.1=Mahina
|
||||
passwordStrength.messageLabel.2=Patas
|
||||
passwordStrength.messageLabel.3=Malakas
|
||||
passwordStrength.messageLabel.4=Napakalakas
|
||||
|
||||
# Quit
|
||||
quit.title=Ihinto ang Application
|
||||
quit.message=May mga naka-unlock na vault
|
||||
quit.description=Pakikumpirma na gusto mong umalis. Ila-lock ng Cryptomator ang lahat ng naka-unlock na vault para maiwasan ang pagkawala ng data.
|
||||
quit.lockAndQuitBtn=Lock at Quit
|
||||
|
||||
# Forced Quit
|
||||
quit.forced.message=Hindi ma-lock ang ilang vault
|
||||
quit.forced.description=Ang pag-lock ng mga vault ay na-block ng mga nakabinbing operasyon o bukas na mga file. Maaari mong pilitin na i-lock ang mga natitirang vault, gayunpaman ang pagkagambala sa I/O ay maaaring magresulta sa pagkawala ng hindi na-save na data.
|
||||
quit.forced.forceAndQuitBtn=Puwersa at Umalis
|
||||
|
||||
# Update Reminder
|
||||
updateReminder.message=I-tsek kung may bagong update?
|
||||
updateReminder.title=Muling iwasto
|
||||
updateReminder.message=I-tsek kung may bagong update?
|
||||
updateReminder.description=Manatiling updated sa mga bagong feature, pag-aayos ng bug, at pagpapahusay sa seguridad. Inirerekomenda naming awtomatikong suriin ang mga update.
|
||||
updateReminder.notNow=Hindi ngayon
|
||||
updateReminder.yesOnce=Oo, Minsan
|
||||
updateReminder.yesAutomatically=Oo, Awtomatiko
|
||||
@@ -22,9 +22,9 @@ error.hyperlink.report=Signaler cette erreur
|
||||
error.technicalDetails=Détails :
|
||||
error.existingSolutionDescription=Cryptomator ne s'attendait pas à ce que cela se produise. Mais nous avons trouvé une solution existante pour cette erreur. Veuillez consulter le lien suivant.
|
||||
error.hyperlink.solution=Rechercher la solution
|
||||
error.lookupPermissionMessage=Cryptomator peut rechercher une solution en ligne. Une requête sera envoyée à notre base de données de problèmes à partir de votre adresse IP.
|
||||
error.lookupPermissionMessage=Cryptomator peut trouver une solution à ce problème en ligne. Cela enverra une requête à notre base de données de problèmes à partir de votre adresse IP.
|
||||
error.dismiss=Ignorer
|
||||
error.lookUpSolution=Rechercher une solution
|
||||
error.lookUpSolution=Rechercher la solution
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=Coffre
|
||||
@@ -40,10 +40,8 @@ traymenu.vault.reveal=Révéler
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Ajouter un coffre
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Créer un nouveau coffre
|
||||
addvaultwizard.welcome.existingButton=Ouvrir un coffre existant
|
||||
## New
|
||||
addvaultwizard.new.title=Ajouter un nouveau coffre
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Choisir un nom pour le coffre
|
||||
addvaultwizard.new.namePrompt=Nom du coffre
|
||||
@@ -93,6 +91,7 @@ addvault.new.readme.accessLocation.2=Ceci est le chemin d'accès de votre coffre
|
||||
addvault.new.readme.accessLocation.3=Tous les fichiers ajoutés à ce volume seront chiffrés par Cryptomator. Vous pouvez l'utiliser comme n'importe quel lecteur/répertoire. Ceci est seulement une vue déchiffrée de son contenu, vos fichiers restent chiffrés dans votre disque dur en permanence.
|
||||
addvault.new.readme.accessLocation.4=Vous pouvez supprimer ce fichier.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Ajouter un coffre existant
|
||||
addvaultwizard.existing.instruction=Choisissez le fichier « vault.cryptomator » de votre volume existant. Si seul le fichier « masterkey.cryptomator » est présent, sélectionnez celui-là.
|
||||
addvaultwizard.existing.chooseBtn=Choisir...
|
||||
addvaultwizard.existing.filePickerTitle=Sélectionnez le fichier correspondant au volume chiffré
|
||||
@@ -138,7 +137,7 @@ unlock.error.customPath.message=Impossible de monter le coffre vers le chemin pe
|
||||
unlock.error.customPath.description.notSupported=Si vous souhaitez continuer à utiliser le chemin personnalisé, veuillez aller dans les préférences et sélectionner un type de volume qui le prend en charge. Sinon, allez dans les options du coffre et choisissez un point de montage pris en charge.
|
||||
unlock.error.customPath.description.notExists=Le chemin de montage personnalisé n'existe pas. Créez-le dans votre système de fichiers local ou modifiez-le dans les options du coffre.
|
||||
unlock.error.customPath.description.inUse=La lettre de lecteur ou le point de montage personnalisé «%s» est déjà utilisé.
|
||||
unlock.error.customPath.description.hideawayNotDir=Le fichier temporaire et caché « %3$s » utilisé pour le déverrouillage n’a pas pu être supprimé. Veuillez vérifier le fichier puis le supprimer manuellement.
|
||||
unlock.error.customPath.description.hideawayNotDir=Le fichier temporaire et caché «%3$s» utilisé pour déverrouiller n'a pas pu être supprimé. Veuillez vérifier le fichier puis le supprimer manuellement.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Votre coffre n'a pas pu être monté au point "%s". Veuillez réessayer ou choisissez un autre point.
|
||||
unlock.error.customPath.description.notEmptyDir=Le chemin de montage personnalisé "%s" n'est pas un dossier vide. Veuillez choisir un dossier vide et réessayez.
|
||||
unlock.error.customPath.description.generic=Vous avez sélectionné un point de montage personnalisé pour ce coffre, mais son utilisation a échoué avec le message : %2$s
|
||||
@@ -154,9 +153,10 @@ hub.auth.loginLink=Vous n'avez pas été redirigé(e) ? Cliquez ici pour l'ouvri
|
||||
hub.receive.message=Traitement de la réponse…
|
||||
hub.receive.description=Cryptomator est en train de recevoir et de traiter la réponse de Hub. Veuillez patienter.
|
||||
### Register Device
|
||||
hub.register.message=Le nom de l'appareil est requis
|
||||
hub.register.description=Il semble que ce soit le premier accès à Hub depuis cet appareil. Afin de l'identifier pour l'autorisation d'accès, vous devez nommer cet appareil.
|
||||
hub.register.message=Nouvel Appareil
|
||||
hub.register.description=Il s'agit du premier accès de cet appareil au Hub. Veuillez l'autoriser à l'aide de votre clé de compte.
|
||||
hub.register.nameLabel=Nom de l'appareil
|
||||
hub.register.invalidAccountKeyLabel=Clé de compte invalide
|
||||
hub.register.occupiedMsg=Nom déjà utilisé
|
||||
hub.register.registerBtn=Confirmer
|
||||
### Registration Success
|
||||
@@ -168,6 +168,11 @@ hub.registerFailed.description=Le processus de nommage a retourné une erreur. P
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Accès refusé
|
||||
hub.unauthorized.description=Votre appareil n'a pas encore été autorisé à accéder à ce coffre. Demandez au propriétaire du coffre de l'autoriser.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Action requise
|
||||
hub.requireAccountInit.description.0=Pour continuer, veuillez compléter les étapes requises
|
||||
hub.requireAccountInit.description.1=Profil utilisateur de Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Licence de Hub invalide
|
||||
hub.invalidLicense.description=Votre instance Cryptomator Hub a une licence invalide. Veuillez informer un administrateur Hub pour la mettre à niveau ou la renouveler.
|
||||
@@ -366,7 +371,9 @@ main.vaultlist.contextMenu.unlock=Déverrouiller…
|
||||
main.vaultlist.contextMenu.unlockNow=Déverrouiller maintenant
|
||||
main.vaultlist.contextMenu.vaultoptions=Afficher les options du volume chiffré
|
||||
main.vaultlist.contextMenu.reveal=Afficher le lecteur
|
||||
main.vaultlist.addVaultBtn=Ajouter un volume chiffré
|
||||
main.vaultlist.addVaultBtn=Ajouter
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Nouveau coffre...
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Coffre existant...
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Merci d'avoir choisi Cryptomator pour protéger vos fichiers. Si vous avez besoin d'aide, consultez nos guides de démarrage :
|
||||
|
||||
@@ -22,7 +22,6 @@ error.message=Produciuse un erro
|
||||
# Tray Menu
|
||||
|
||||
# Add Vault Wizard
|
||||
## Welcome
|
||||
## New
|
||||
### Name
|
||||
### Location
|
||||
@@ -49,6 +48,7 @@ error.message=Produciuse un erro
|
||||
### Registration Success
|
||||
### Registration Failed
|
||||
### Unauthorized
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
|
||||
# Lock
|
||||
|
||||
@@ -20,6 +20,11 @@ error.description=Cryptomator לא ציפתה שזה יקרה. ניתן לחפש
|
||||
error.hyperlink.lookup=חיפוש שגיאה
|
||||
error.hyperlink.report=דיווח על שגיאה
|
||||
error.technicalDetails=פרטים:
|
||||
error.existingSolutionDescription=Cryptomator לא ציפה שזה יקרה. אך מצאנו פתרון קיים לשגיאה זו. בבקשה לבדוק את הקישור הבא.
|
||||
error.hyperlink.solution=חפש את הפתרון
|
||||
error.lookupPermissionMessage=Cryptomator יכול לחפש פתרון לבעיה זו באינטרנט. זה ישלח בקשה למסד הנתונים של הבעיה מכתובת ה-IP שלך.
|
||||
error.dismiss=שחרור
|
||||
error.lookUpSolution=חפש את הפתרון
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=כספת
|
||||
@@ -35,10 +40,8 @@ traymenu.vault.reveal=חשוף
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=הוספת כספת
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=צור כספת חדשה
|
||||
addvaultwizard.welcome.existingButton=פתיחת כספת קיימת
|
||||
## New
|
||||
addvaultwizard.new.title=הוסף ארגז חדש
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=בחירת שם עבור הכספת
|
||||
addvaultwizard.new.namePrompt=שם הכספת
|
||||
@@ -60,6 +63,11 @@ addvaultwizard.new.validCharacters.chars=תווי מילים (למשל: a, ж or
|
||||
addvaultwizard.new.validCharacters.numbers=מספרים
|
||||
addvaultwizard.new.validCharacters.dashes=מקף (%s) או קו תחתון (%s)
|
||||
### Expert Settings
|
||||
addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=הפעל הגדרות מתקדמות
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.invalid=הזן ערך בין 36 ל-220 (ברירת המחדל 220)
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=פתח את התיעוד כדי ללמוד עוד.
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.title=האורך המרבי של שמות הקבצים המוצפנים
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.valid=בתוקף
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=צור כספת חדשה
|
||||
addvaultwizard.new.generateRecoveryKeyChoice=לא תיהיה אפשרות לקבל גישה למידע שלך בלי הסיסמה שלך. האם תרצה/י מפתח שחזור למקרה ותאבד/י את הסיסמה שלך?
|
||||
@@ -83,6 +91,7 @@ addvault.new.readme.accessLocation.2=זהו מיקום גישה לכספת של
|
||||
addvault.new.readme.accessLocation.3=כל קובץ אשר יצורף לספרייה זו יעבור הצפנה באמצעות Cryptomator. את/ה תוכל/י לעבוד עליו כמו עם כל קבוץ/מחיצה רגילים. זהו מצב הצגה מפוענח של התוכן, הקבצים שלך נשארים מוצפנים על הדיסק הקשיח שלך בכל רגע.
|
||||
addvault.new.readme.accessLocation.4=תרגיש/י בנוח להסיר את הקובץ הזה.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=הוסף כספת קיימת
|
||||
addvaultwizard.existing.instruction=בחר את קובץ "vault.cryptomator" של כספת קיימת. אם קיים קובץ בשם "masterkey.cryptomator" בלבד, בחר/י אותו במקום.
|
||||
addvaultwizard.existing.chooseBtn=בחר...
|
||||
addvaultwizard.existing.filePickerTitle=בחר קובץ כספת
|
||||
@@ -127,6 +136,10 @@ unlock.success.revealBtn=חשוף את הכונן
|
||||
unlock.error.customPath.message=כשלון בקישור הכספת לנתיב הידני שהוגדר
|
||||
unlock.error.customPath.description.notSupported=אם ברצונך להשתמש בנתיב ידני, אנא גש להעדפות ובחר סוג volume שתומך בכך. אחרת, לך לאפשרויות הכספת ובחר אפשרות יעד קישור נתמך.
|
||||
unlock.error.customPath.description.notExists=יעד הקישור הידני לא קיים. או שתיצור אותו במערכת הקבצים המקומית או שנה אותו באפשרויות הכספת.
|
||||
unlock.error.customPath.description.inUse=אות דיסק או הנתיב המותאם "%s" כבר בשימוש.
|
||||
unlock.error.customPath.description.hideawayNotDir=הקובץ הזמני, הנסתר %3$s עבור פתיחה לא ניתן להסרה. יש לבדוק את הקובץ ולמחוק אותו ידנית.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=הקלדת חנייה שלך לא יכולה להיות מחוברת לנתיב "%s". אנא נסה שוב או בחר נתיב אחר.
|
||||
unlock.error.customPath.description.notEmptyDir=הנתיב המותאם "%s" אינו תיקייה ריקה. אנא בחר תיקייה ריקה ונסה שוב.
|
||||
## Hub
|
||||
hub.noKeychain.message=לא ניתן לגשת למפתח המכשיר
|
||||
hub.noKeychain.description=כדאי לשחרר כספות האב נדרש מפתח מכשיר שיאובטח בצרור מפתחות. כדאי להמשיך, אפשר ״%s״ ובחר את צרור המפתחות בהעדפות.
|
||||
@@ -139,8 +152,6 @@ hub.auth.loginLink=לא הופנת? לחצן כאן לפתיחה.
|
||||
hub.receive.message=מעבד תשובה…
|
||||
hub.receive.description=Cryptomator מקבל ומעבד את התשובה מה- האב. אנא המתן.
|
||||
### Register Device
|
||||
hub.register.message=שם המכשיר נדרש
|
||||
hub.register.description=נראה שזו הגישה הראשונה ל- האב ממכשיר זה. כדי לזהות אותך למתן הרשאות, עליך לתת שם למכשיר זה.
|
||||
hub.register.nameLabel=שם מכשיר
|
||||
hub.register.occupiedMsg=שם זה נמצא כבר בשימוש
|
||||
hub.register.registerBtn=אישור
|
||||
@@ -153,6 +164,7 @@ hub.registerFailed.description=ארעה שגיאה בתהליך עם השם. ל
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=הגישה נדחתה
|
||||
hub.unauthorized.description=המכשיר שלך טרם אושר לגשת לכספת הזאת. יש לבקש אישור גישה מבעל הכספת.
|
||||
### Requires Account Initialization
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=רישיון האב לא תקף
|
||||
hub.invalidLicense.description=הרישיון שמותקן במופע ה- Cryptomator האב שלך אינו תקף. אנא ידע את מנהל ההאב שלך לשדרג או לחדש את הרישיון.
|
||||
@@ -351,7 +363,8 @@ main.vaultlist.contextMenu.unlock=שחרר נעילה…
|
||||
main.vaultlist.contextMenu.unlockNow=בטל נעילה כעת
|
||||
main.vaultlist.contextMenu.vaultoptions=הצג את אפשרויות הכספת
|
||||
main.vaultlist.contextMenu.reveal=חשוף את הכונן
|
||||
main.vaultlist.addVaultBtn=יצירת כספת
|
||||
main.vaultlist.addVaultBtn=הוספה
|
||||
main.vaultlist.addVaultBtn.menuItemNew=פתיחת כספת...
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=תודה שבחרת ב- Cryptomator להגן על הקבצים שלך. אם אתה זקוק לסיוע, אנא עיין במדריכים שלנו:
|
||||
@@ -457,6 +470,8 @@ recoveryKey.recover.resetSuccess.message=איפוס סיסמה הצליח
|
||||
recoveryKey.recover.resetSuccess.description=ניתן לפתוח את הכספת עם הסיסמה החדשה.
|
||||
|
||||
# Convert Vault
|
||||
convertVault.convert.convertBtn.before=להמיר
|
||||
convertVault.success.message=המרה הושלמה בהצלחה
|
||||
|
||||
# New Password
|
||||
newPassword.promptText=הקש סיסמה חדשה
|
||||
@@ -481,4 +496,7 @@ quit.forced.message=חלק מה vaults לא היו ניתנים לנעילה
|
||||
quit.forced.description=נעילה כספות נחסמה על ידי תהליכים ממתינים או קבצים פתוחים. אתה יכול לנעול את הכספת בכוח, אולם הפרעה לפעולת קריאה וכתיבה עשויה לגרום לאובדן מידע לא שמור.
|
||||
quit.forced.forceAndQuitBtn=נעילה בכח ויציאה
|
||||
|
||||
# Update Reminder
|
||||
# Update Reminder
|
||||
updateReminder.title=בדיקת עדכון
|
||||
updateReminder.message=חפש עדכונים?
|
||||
updateReminder.notNow=לא עכשיו
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user