Compare commits

...

56 Commits
1.0.1 ... 1.0.5

Author SHA1 Message Date
Sebastian Stenzel
5f22bd5785 Increased version number to 1.0.5 2016-05-10 14:55:02 +02:00
Sebastian Stenzel
7312c0f9db repeated commit 86000ac 2016-05-10 14:53:18 +02:00
Sebastian Stenzel
b77d4b5ae2 fixes #264 2016-05-10 14:33:21 +02:00
Sebastian Stenzel
7b6c5318c5 fixes #263 2016-05-10 14:31:55 +02:00
Sebastian Stenzel
6006d65ce0 new ant kit using a custom launcher binary due to #265 2016-05-10 14:26:06 +02:00
Sebastian Stenzel
ad2c9116b9 Release 1.0.4 2016-05-03 16:50:18 +02:00
Sebastian Stenzel
8e24745b3e Merge branch 'master' into patches-1.0.x 2016-05-03 16:46:18 +02:00
Sebastian Stenzel
08f664e3df Throttle calls to Settings.save() 2016-05-03 16:44:22 +02:00
Tobias Hagemann
b6d1d1dc22 updated linux app icon [ci skip] 2016-05-03 16:07:30 +02:00
Sebastian Stenzel
a0ef02b95c fixes #237 2016-05-03 13:17:45 +02:00
Sebastian Stenzel
a6cefe67c4 setting default port to 42427 [ci skip] 2016-05-03 10:48:24 +02:00
Sebastian Stenzel
be2b63ab2a support for UTF-8 localization files 2016-05-02 22:28:49 +02:00
Sebastian Stenzel
78f11b4a5e added korean localization [ci skip] 2016-05-02 16:01:28 +02:00
Sebastian Stenzel
0f20c7c3c9 fixes #209 2016-05-02 12:36:31 +02:00
Sebastian Stenzel
d4235174f7 imported localizations from POEditor fixes #231 and #234 (ci skip) 2016-05-02 11:37:40 +02:00
Sebastian Stenzel
f16be84aa3 restored bash-based webdav mounting for OS X before 10.10 (issue #211 - to be tested) 2016-05-02 11:11:42 +02:00
Sebastian Stenzel
833f2d8566 fixed travis test coverage configuration 2016-04-27 01:18:37 +02:00
Sebastian Stenzel
c02a63878e new method to calculate test coverage (across modules) 2016-04-27 01:14:41 +02:00
Sebastian Stenzel
ac9fe28967 Merge branch 'master' into patches-1.0.x
Release 1.0.3d
2016-04-22 10:55:03 +02:00
Sebastian Stenzel
515755d84a updated antkit to support deb and rpm packages [ci skip] 2016-04-22 10:53:51 +02:00
Sebastian Stenzel
fef19fe6b3 Merge branch 'master' into patches-1.0.x 2016-04-14 22:39:56 +02:00
Sebastian Stenzel
5f56dacc4e adjusted travis configuration [ci skip] 2016-04-14 22:39:19 +02:00
Sebastian Stenzel
aa249dabb5 technical release 1.0.3c 2016-04-14 22:28:12 +02:00
Sebastian Stenzel
06a5bed6e3 Merge branch 'master' into patches-1.0.x 2016-04-14 22:27:15 +02:00
Sebastian Stenzel
02f1ffc6bf updated antkit creation (tarball no longer contains a base directory) 2016-04-14 22:26:36 +02:00
Sebastian Stenzel
de9af9e303 fixed funny detail label in vault list, if vault is not located inside home directory 2016-04-13 15:26:27 +02:00
Sebastian Stenzel
e2bc71a0bc added spanish translation template [ci skip] 2016-04-11 14:17:33 +02:00
Sebastian Stenzel
e528f6827c Added translation button [ci skip] 2016-04-11 14:02:11 +02:00
Sebastian Stenzel
2882ae8ef8 Update localization_de.properties (POEditor.com) 2016-04-11 13:46:51 +02:00
Sebastian Stenzel
e37f7cea1a Merge pull request #227 from jncharon/master
French translation
2016-04-11 10:55:26 +02:00
jncharon
9b4ee10155 Adjustments to the french translation 2016-04-10 15:53:53 +02:00
jncharon
c9d970955c French translaction 2016-04-10 15:10:47 +02:00
Sebastian Stenzel
9e0afd36c4 Merge branch 'master' into patches-1.0.x [ci skip] 2016-04-10 02:42:25 +02:00
Sebastian Stenzel
0e523599a3 add execution phase 2016-04-10 02:41:51 +02:00
Sebastian Stenzel
1df6589dd7 make sure, .tar.gz is built on travis 2016-04-10 02:36:10 +02:00
Sebastian Stenzel
fb60c97fd3 Merge branch 'master' into patches-1.0.x 2016-04-10 02:19:52 +02:00
Sebastian Stenzel
90cd149be8 Update .travis.yml 2016-04-10 02:19:01 +02:00
Sebastian Stenzel
89c04ad83b test release 1.0.3b 2016-04-10 02:07:52 +02:00
Sebastian Stenzel
f2d383a211 Merge branch 'master' into patches-1.0.x 2016-04-10 01:56:13 +02:00
Sebastian Stenzel
73fde5d020 null-safe status indicators 2016-04-10 01:54:44 +02:00
Sebastian Stenzel
5c0857e98e build ant-kit on tag/release [ci skip] 2016-04-10 01:53:44 +02:00
Sebastian Stenzel
3e87b9c0c6 oracle jdk8 + jce on trusty 2016-04-10 00:28:11 +02:00
Sebastian Stenzel
a1d0b6b1d3 trying to build with openjdk8 on trusty 2016-04-10 00:22:56 +02:00
Tobias Hagemann
b0d4b2e403 fixed support mail link in code of conduct [ci skip] 2016-04-06 00:04:36 +02:00
Tobias Hagemann
6996d36ea2 added issue template, contribution guide, code of conduct [ci skip] 2016-04-05 12:28:36 +02:00
Sebastian Stenzel
f77ba908da Patch 1.0.3 2016-03-25 16:43:58 +01:00
Sebastian Stenzel
9890789c51 Merge branch 'master' into patches-1.0.x
# Conflicts:
#	main/commons-test/pom.xml
#	main/commons/pom.xml
#	main/filesystem-api/pom.xml
#	main/filesystem-crypto-integration-tests/pom.xml
#	main/filesystem-crypto/pom.xml
#	main/filesystem-inmemory/pom.xml
#	main/filesystem-invariants-tests/pom.xml
#	main/filesystem-nameshortening/pom.xml
#	main/filesystem-nio/pom.xml
#	main/filesystem-stats/pom.xml
#	main/frontend-api/pom.xml
#	main/frontend-webdav/pom.xml
#	main/pom.xml
#	main/uber-jar/pom.xml
#	main/ui/pom.xml
2016-03-25 16:42:44 +01:00
Sebastian Stenzel
a385f2eaef fixes #174 2016-03-25 16:41:30 +01:00
Sebastian Stenzel
553cb5ee3d Migration of vault bundles ending on ".cryptomator" to normal directories. 2016-03-24 22:51:40 +01:00
Markus Kreusch
d0dc8819f4 No longer using TrayIcon on linux systems
* Reason: TrayIcon not supported well on linux and caused problems
* Renamed TrayIconUtil to ExitUtil
* fixes #177
2016-03-24 14:20:10 +01:00
Sebastian Stenzel
221deeda25 removed .cryptomator directory extension when creating new vaults 2016-03-22 21:36:26 +01:00
Sebastian Stenzel
86000ac454 removed test dependencies from main project 2016-03-22 13:04:46 +01:00
Sebastian Stenzel
d026afec35 adjusted snapshot version [ci skip] 2016-03-21 16:49:58 +01:00
Sebastian Stenzel
0d57ebb24a Patch 1.0.2 2016-03-21 16:44:01 +01:00
Sebastian Stenzel
f12168ca94 allow deletion of corrupt directories, whose dir-file exists but physical directory doesn't. related to #181 2016-03-21 15:49:44 +01:00
Sebastian Stenzel
d397f59565 fixes #181 2016-03-21 14:46:15 +01:00
94 changed files with 2801 additions and 410 deletions

View File

@@ -1,3 +1,7 @@
sudo: required
dist: trusty
language: java
jdk:
@@ -8,11 +12,11 @@ env:
- secure: "Lgj042RD0X3rB8VZVZLWP1GetLhjd3PqI5JbJMlzgHJpDI6RkFIBLN9SWAGmkLPCehIp2zA5tu9+UVy0NNMxm9xz6SyjMCaxS28/fnYEXaNmwwDSF6O6gLUbdxyzoYIFPYOPmFxpzhebqnNIsxaM29oZpgRgUGqosCczQxiB+Ng=" #coveralls
- secure: "IfYURwZaDWuBDvyn47n0k1Zod/IQw1FF+CS5nnV08Q+NfC3vGGJMwV8m59XnbfwnWGxwvCaAbk4qP6s6+ijgZNKkvgfFMo3rfTok5zt43bIqgaFOANYV+OC/1c59gYD6ZUxhW5iNgMgU3qdsRtJuwSmfkVv/jKyLGfAbS4kN8BA=" #coverity
before_install: "curl -L --cookie 'oraclelicense=accept-securebackup-cookie;' http://download.oracle.com/otn-pub/java/jce/8/jce_policy-8.zip -o /tmp/policy.zip && sudo unzip -j -o /tmp/policy.zip *.jar -d `jdk_switcher home oraclejdk8`/jre/lib/security && rm /tmp/policy.zip"
before_install: "curl -L --cookie 'oraclelicense=accept-securebackup-cookie;' http://download.oracle.com/otn-pub/java/jce/8/jce_policy-8.zip -o /tmp/policy.zip && sudo unzip -j -o /tmp/policy.zip *.jar -d `jdk_switcher home oraclejdk8`/jre/lib/security && rm /tmp/policy.zip"
script: mvn -fmain/pom.xml clean test
after_success: mvn -fmain/pom.xml clean test jacoco:report coveralls:report
after_success: mvn -fmain/pom.xml -Ptest-coverage clean test jacoco:report-aggregate coveralls:report
notifications:
webhooks:
@@ -27,7 +31,7 @@ notifications:
on_success: change
on_failure: always
before_deploy: mvn -fmain/pom.xml -Puber-jar clean package -DskipTests
before_deploy: mvn -fmain/pom.xml -Prelease clean package -DskipTests
addons:
coverity_scan:
@@ -42,7 +46,9 @@ deploy:
prerelease: false
api_key:
secure: "ZjE1j93v3qbPIe2YbmhS319aCbMdLQw0HuymmluTurxXsZtn9D4t2+eTr99vBVxGRuB5lzzGezPR5zjk5W7iHF7xhwrawXrFzr2rPJWzWFt0aM+Ry2njU1ROTGGXGTbv4anWeBlgMxLEInTAy/9ytOGNJlec83yc0THpOY2wxnk="
file: main/uber-jar/target/Cryptomator-$TRAVIS_TAG.jar
file:
- "main/uber-jar/target/Cryptomator-$TRAVIS_TAG.jar"
- "main/ant-kit/target/antkit.tar.gz"
skip_cleanup: true
on:
repo: cryptomator/cryptomator

74
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at support@cryptomator.org. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

33
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,33 @@
# Contributing to Cryptomator
## Did you find a bug?
- Ensure you're running the latest version of Cryptomator.
- Ensure the bug is related to the desktop version of Cryptomator. Bugs concerning the Cryptomator iOS app can be reported on the [Cryptomator for iOS issues list](https://github.com/cryptomator/cryptomator-ios/issues).
- Ensure the bug was not [already reported](https://github.com/cryptomator/cryptomator/issues). You can also check out our [FAQ](https://cryptomator.org/faq/) and our [Wiki](https://github.com/cryptomator/cryptomator/wiki).
- If you're unable to find an open issue addressing the problem, [submit a new one](https://github.com/cryptomator/cryptomator/issues/new).
## Do you have questions?
- Ask questions by [submitting a new issue](https://github.com/cryptomator/cryptomator/issues/new).
- [Contact us](https://cryptomator.org/contact/) directly by writing an email. Wir sprechen auch Deutsch!
- Have a chat with us on [Gitter](https://gitter.im/cryptomator/cryptomator).
## Did you write a patch that fixes a bug?
- Open a new pull request with the patch.
- Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
## Do you intend to add a new feature or change an existing one?
- Suggest your change by [submitting a new issue](https://github.com/cryptomator/cryptomator/issues/new) and start writing code.
## Code of Conduct
Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/master/CODE_OF_CONDUCT.md).
## Above all, thank you for your contributions
Thank you for taking the time to contribute to the project! :+1:
Cryptomator Team

19
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,19 @@
### Basic Info
- I'm running Cryptomator on: [Windows, OS X, and/or Debian (or other Linux Distribution), don't forget the version]
- I'm using Cryptomator in version: [you can check the version in the settings of Cryptomator]
### Description
[description of the bug, question or feature - what did you do? what problem occurred? etc.]
### Log File (optional)
```
[insert relevant parts of the log file here if applicable,
don't forget to redact sensitive information
on Windows: %appdata%/Cryptomator/cryptomator.log
on OS X: ~/Library/Logs/Cryptomator/cryptomator.log
on Debian: ~/.Cryptomator/cryptomator.log]
```

View File

@@ -1,44 +1,67 @@
Cryptomator
====================
![cryptomator](cryptomator.png)
[![Build Status](https://travis-ci.org/cryptomator/cryptomator.svg?branch=master)](https://travis-ci.org/cryptomator/cryptomator)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/cryptomator-cryptomator/badge.svg?flat=1)](https://scan.coverity.com/projects/cryptomator-cryptomator)
[![Coverage Status](https://coveralls.io/repos/github/cryptomator/cryptomator/badge.svg?branch=master)](https://coveralls.io/github/cryptomator/cryptomator?branch=master)
[![Join the chat at https://gitter.im/cryptomator/cryptomator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/cryptomator/cryptomator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Twitter](https://img.shields.io/badge/twitter-@Cryptomator-blue.svg?style=flat)](http://twitter.com/Cryptomator)
[![POEditor](https://img.shields.io/badge/POEditor-Help%20Translate-blue.svg?style=flat)](https://poeditor.com/join/project/bHwbvJmx0E)
Multiplatform transparent client-side encryption of your files in the cloud.
Multi-platform transparent client-side encryption of your files in the cloud.
If you want to take a look at the current beta version, go ahead and get your copy of cryptomator on [Cryptomator.org](https://cryptomator.org) or clone and build Cryptomator using Maven (instructions below).
Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator.org/) or clone and build Cryptomator using Maven (instructions below).
## Features
- Totally transparent: Just work on the encrypted volume, as if it was an USB flash drive
- Works with Dropbox, OneDrive (Skydrive), Google Drive and any other cloud storage, that syncs with a local directory.
- In fact it works with any directory. You can use it to encrypt as many folders as you like
- Works with Dropbox, Google Drive, OneDrive, and any other cloud storage service that synchronizes with a local directory
- Open Source means: No backdoors, control is better than trust
- Client-side: No accounts, no data shared with any online service
- Totally transparent: Just work on the virtual drive as if it were a USB flash drive
- AES encryption with 256-bit key length
- Client-side. No accounts, no data shared with any online service
- Filenames get encrypted too
- No need to provide credentials for any 3rd party service
- Open Source means: No backdoors. Control is better than trust
- Use as many encrypted folders in your Dropbox as you want. Each having individual passwords
- No commercial interest, no government agency, no wasted taxpayers' money ;-)
- Filenames get encrypted, too
- Use as many vaults in your Dropbox as you want, each having individual passwords
### Privacy
- 256 bit keys (unlimited strength policy bundled with native binaries - 128-bit elsewhere)
- 256-bit keys (unlimited strength policy bundled with native binaries)
- Scrypt key derivation
- Cryptographically secure random numbers for salts, IVs and the master key of course
- Sensitive data is swiped from the heap asap
- Cryptographically secure random numbers for salts, IVs and the masterkey of course
- Sensitive data is wiped from the heap asap
- Lightweight: [Complexity kills security](https://www.schneier.com/essays/archives/1999/11/a_plea_for_simplicit.html)
### Consistency
- HMAC over file contents to recognize changed ciphertext before decryption
- I/O operations are transactional and atomic, if the file systems support it
- Each file contains all information needed for decryption (except for the key of course). No common metadata means no [SPOF](http://en.wikipedia.org/wiki/Single_point_of_failure)
- I/O operations are transactional and atomic, if the filesystems support it
- Each file contains all information needed for decryption (except for the key of course), no common metadata means no [SPOF](http://en.wikipedia.org/wiki/Single_point_of_failure)
### Security Architecture
For more information on the security details visit [cryptomator.org](https://cryptomator.org/architecture/).
## Building
#### Dependencies
### Dependencies
* Java 8 + JCE unlimited strength policy files (needed for 256-bit keys)
* Maven 3
* Optional: OS-dependent build tools for native packaging (See [Windows](https://github.com/cryptomator/cryptomator-win), [OS X](https://github.com/cryptomator/cryptomator-osx), [Debian](https://github.com/cryptomator/cryptomator-deb))
* Optional: OS-dependent build tools for native packaging (see [Windows](https://github.com/cryptomator/cryptomator-win), [OS X](https://github.com/cryptomator/cryptomator-osx), [Debian](https://github.com/cryptomator/cryptomator-deb))
### Run Maven
```
cd main
mvn clean install
```
## Contributing to Cryptomator
Please read our [contribution guide](https://github.com/cryptomator/cryptomator/blob/master/CONTRIBUTING.md), if you would like to report a bug, ask a question or help us with coding.
## Code of Conduct
Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/master/CODE_OF_CONDUCT.md).
## License
Distributed under the MIT X Consortium license. See the LICENSE file for more info.
Distributed under the MIT X Consortium license. See the `LICENSES/MIT-X-Consortium-License.txt` file for more info.

BIN
cryptomator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

1
main/ant-kit/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target/

37
main/ant-kit/assembly.xml Normal file
View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>tarball</id>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>target/libs</directory>
<includes>
<include>*.jar</include>
</includes>
<outputDirectory>libs</outputDirectory>
</fileSet>
<fileSet>
<directory>target/fixed-binaries</directory>
<filtered>false</filtered>
<outputDirectory>fixed-binaries</outputDirectory>
<fileMode>755</fileMode>
</fileSet>
<fileSet>
<directory>target/package</directory>
<filtered>false</filtered>
<outputDirectory>package</outputDirectory>
</fileSet>
<fileSet>
<directory>target</directory>
<includes>
<include>build.xml</include>
</includes>
<filtered>false</filtered>
<outputDirectory>.</outputDirectory>
</fileSet>
</fileSets>
</assembly>

103
main/ant-kit/pom.xml Normal file
View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2016 Sebastian Stenzel
This file is licensed under the terms of the MIT license.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.5</version>
</parent>
<artifactId>ant-kit</artifactId>
<packaging>pom</packaging>
<name>Cryptomator Ant Build Kit</name>
<description>Builds a package that can be built with Ant locally</description>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>ui</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- copy libraries to target/libs/: -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-libs</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- copy resources to target/: -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}</outputDirectory>
<escapeString>\</escapeString>
<encoding>UTF-8</encoding>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>fixed-binaries/**</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>fixed-binaries/**</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<!-- create antkit.tar.gz: -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
<finalName>antkit</finalName>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="Cryptomator" default="create-jar" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant">
<taskdef uri="javafx:com.sun.javafx.tools.ant" resource="com/sun/javafx/tools/ant/antlib.xml" classpath="\${java.class.path}:\${java.home}/../lib/ant-javafx.jar:." />
<!-- Define application to build -->
<fx:application id="Cryptomator" name="Cryptomator" version="${project.version}" mainClass="org.cryptomator.ui.Cryptomator" />
<!-- Create main application jar -->
<target name="create-jar">
<fx:jar destfile="antbuild/Cryptomator-${project.version}.jar">
<fx:application refid="Cryptomator" />
<fx:fileset dir="libs" includes="ui-${project.version}.jar" />
<fx:resources>
<fx:fileset dir="libs" type="jar" includes="*.jar" excludes="ui-${project.version}.jar" />
</fx:resources>
<fx:manifest>
<fx:attribute name="Implementation-Vendor" value="cryptomator.org" />
<fx:attribute name="Implementation-Title" value="Cryptomator"/>
<fx:attribute name="Implementation-Version" value="${project.version}" />
</fx:manifest>
</fx:jar>
</target>
<!-- Create native image -->
<target name="create-linux-image-with-jvm" depends="create-jar">
<fx:deploy nativeBundles="image" outdir="antbuild" outfile="Cryptomator-${project.version}" verbose="true">
<fx:application refid="Cryptomator" />
<fx:platform j2se="8.0">
<fx:property name="cryptomator.logPath" value="~/.Cryptomator/cryptomator.log" />
<fx:jvmarg value="-Xmx512m"/>
</fx:platform>
<fx:resources>
<fx:fileset dir="antbuild" type="jar" includes="Cryptomator-${project.version}.jar" />
<fx:fileset dir="libs" type="jar" includes="*.jar" excludes="ui-${project.version}.jar"/>
</fx:resources>
</fx:deploy>
</target>
<!-- Create Debian package -->
<target name="deb" depends="create-jar">
<fx:deploy nativeBundles="deb" outdir="antbuild" outfile="Cryptomator-${project.version}" verbose="true">
<fx:application refid="Cryptomator" />
<fx:info title="Cryptomator" vendor="cryptomator.org" copyright="cryptomator.org" license="MIT" category="Utility">
<fx:association mimetype="application/x-vnd.cryptomator-vault-metadata" extension="cryptomator" description="Cryptomator Vault Metadata" />
</fx:info>
<fx:platform j2se="8.0">
<fx:property name="cryptomator.logPath" value="~/.Cryptomator/cryptomator.log" />
<fx:jvmarg value="-Xmx1048m"/>
</fx:platform>
<fx:resources>
<fx:fileset dir="antbuild" type="jar" includes="Cryptomator-${project.version}.jar" />
<fx:fileset dir="libs" type="jar" includes="*.jar" excludes="ui-${project.version}.jar"/>
<fx:fileset dir="fixed-binaries" type="data" includes="linux-launcher-*" arch=""/>
</fx:resources>
<fx:permissions elevated="false" />
<fx:preferences install="true" />
</fx:deploy>
</target>
<!-- Create Red Hat package -->
<target name="rpm" depends="create-jar">
<fx:deploy nativeBundles="rpm" outdir="antbuild" outfile="Cryptomator-${project.version}" verbose="true">
<fx:application refid="Cryptomator" />
<fx:info title="Cryptomator" vendor="cryptomator.org" copyright="cryptomator.org" license="MIT" category="Utility">
<fx:association mimetype="application/x-vnd.cryptomator-vault-metadata" extension="cryptomator" description="Cryptomator Vault Metadata" />
</fx:info>
<fx:platform j2se="8.0">
<fx:property name="cryptomator.logPath" value="~/.Cryptomator/cryptomator.log" />
<fx:jvmarg value="-Xmx1048m"/>
</fx:platform>
<fx:resources>
<fx:fileset dir="antbuild" type="jar" includes="Cryptomator-${project.version}.jar" />
<fx:fileset dir="libs" type="jar" includes="*.jar" excludes="ui-${project.version}.jar"/>
</fx:resources>
<fx:permissions elevated="false" />
<fx:preferences install="true" />
</fx:deploy>
</target>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,16 @@
Package: APPLICATION_PACKAGE
Version: APPLICATION_VERSION
Section: contrib/utils
Maintainer: Sebastian Stenzel <sebastian.stenzel@gmail.com>
Homepage: https://cryptomator.org
Vcs-Git: https://github.com/totalvoidness/cryptomator.git
Vcs-Browser: https://github.com/totalvoidness/cryptomator
Priority: optional
Architecture: APPLICATION_ARCH
Provides: APPLICATION_PACKAGE
Installed-Size: APPLICATION_INSTALLED_SIZE
Depends: gvfs-bin, gvfs-backends, gvfs-fuse, xdg-utils
Description: Multi-platform client-side encryption of your cloud files.
Cryptomator provides free client-side AES encryption for your cloud files.
Create encrypted vaults, which get mounted as virtual volumes. Whatever
you save on one of these volumes will end up encrypted inside your vault.

View File

@@ -0,0 +1,23 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cryptomator
Source: <https://github.com/totalvoidness/cryptomator>
Copyright: 2015 Sebastian Stenzel <sebastian.stenzel@gmail.com> and contributors.
License: MIT
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,50 @@
#!/bin/sh
# postinst script for APPLICATION_NAME
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
configure)
echo Adding shortcut to the menu
SECONDARY_LAUNCHERS_INSTALL
APP_CDS_CACHE
xdg-desktop-menu install --novendor /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
FILE_ASSOCIATION_INSTALL
rm /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
if [ $(uname -m) = "x86_64" ]; then
mv /opt/APPLICATION_FS_NAME/app/linux-launcher-x64 /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
else
mv /opt/APPLICATION_FS_NAME/app/linux-launcher-x86 /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
fi
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

View File

@@ -0,0 +1,54 @@
Summary: APPLICATION_SUMMARY
Name: APPLICATION_PACKAGE
Version: APPLICATION_VERSION
Release: 1
License: APPLICATION_LICENSE_TYPE
Vendor: APPLICATION_VENDOR
Prefix: /opt
Provides: APPLICATION_PACKAGE
Requires: ld-linux.so.2 libX11.so.6 libXext.so.6 libXi.so.6 libXrender.so.1 libXtst.so.6 libasound.so.2 libc.so.6 libdl.so.2 libgcc_s.so.1 libm.so.6 libpthread.so.0 libthread_db.so.1
Autoprov: 0
Autoreq: 0
#avoid ARCH subfolder
%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
#comment line below to enable effective jar compression
#it could easily get your package size from 40 to 15Mb but
#build time will substantially increase and it may require unpack200/system java to install
%define __jar_repack %{nil}
%description
APPLICATION_DESCRIPTION
%prep
%build
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}/opt
cp -r %{_sourcedir}/APPLICATION_FS_NAME %{buildroot}/opt
%files
APPLICATION_LICENSE_FILE
/opt/APPLICATION_FS_NAME
%post
SECONDARY_LAUNCHERS_INSTALL
APP_CDS_CACHE
xdg-desktop-menu install --novendor /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
FILE_ASSOCIATION_INSTALL
rm /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
if [ $(uname -m) = "x86_64" ]; then
mv /opt/APPLICATION_FS_NAME/app/linux-launcher-x64 /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
else
mv /opt/APPLICATION_FS_NAME/app/linux-launcher-x86 /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
fi
%preun
SECONDARY_LAUNCHERS_REMOVE
xdg-desktop-menu uninstall --novendor /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
FILE_ASSOCIATION_REMOVE
%clean

View File

@@ -1,23 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2015 Markus Kreusch This file is licensed under the terms
of the MIT license. See the LICENSE.txt file for more info. -->
<!--
Copyright (c) 2015 Markus Kreusch
This file is licensed under the terms of the MIT license.
See the LICENSE.txt file for more info.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>commons-test</artifactId>
<name>Cryptomator common test dependencies</name>
<description>Shared utilities for tests</description>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>commons</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<dependency>
<groupId>de.bechte.junit</groupId>
<artifactId>junit-hierarchicalcontextrunner</artifactId>
@@ -26,11 +38,6 @@
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>commons</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,29 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2015 Markus Kreusch This file is licensed under the terms
of the MIT license. See the LICENSE.txt file for more info. -->
<!--
Copyright (c) 2015 Markus Kreusch
This file is licensed under the terms of the MIT license.
See the LICENSE.txt file for more info.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>commons</artifactId>
<name>Cryptomator common</name>
<description>Shared utilities</description>
<dependencies>
<!-- Libs -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- DI -->
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
</dependency>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<scope>provided</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.bechte.junit</groupId>
<artifactId>junit-hierarchicalcontextrunner</artifactId>
@@ -35,4 +60,13 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,21 @@
package org.cryptomator.common;
import java.util.Comparator;
import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class CommonsModule {
@Provides
@Singleton
@Named("SemVer")
Comparator<String> providesSemVerComparator() {
return new SemVerComparator();
}
}

View File

@@ -6,7 +6,7 @@
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.ui.util;
package org.cryptomator.common;
import java.util.Comparator;

View File

@@ -6,10 +6,11 @@
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.ui.util;
package org.cryptomator.common;
import java.util.Comparator;
import org.cryptomator.common.SemVerComparator;
import org.junit.Assert;
import org.junit.Test;

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>filesystem-api</artifactId>
<name>Cryptomator filesystem: API</name>

View File

@@ -13,6 +13,7 @@ import java.util.Optional;
import java.util.stream.Stream;
import org.cryptomator.common.WeakValuedCache;
import org.cryptomator.common.streams.AutoClosingStream;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
@@ -35,7 +36,7 @@ public abstract class DelegatingFolder<D extends DelegatingFolder<D, F>, F exten
@Override
public Stream<? extends Node> children() throws UncheckedIOException {
return Stream.concat(folders(), files());
return AutoClosingStream.from(Stream.concat(folders(), files()));
}
@Override

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 Sebastian Stenzel
This file is licensed under the terms of the MIT license.
See the LICENSE.txt file for more info.
Contributors:
Sebastian Stenzel - initial API and implementation
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.5</version>
</parent>
<artifactId>filesystem-charsets</artifactId>
<name>Cryptomator filesystem: Filename charset compatibility layer</name>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-api</artifactId>
</dependency>
<!-- Tests -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>commons-test</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-inmemory</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.filesystem.charsets;
import java.io.UncheckedIOException;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.delegating.DelegatingFile;
class NormalizedNameFile extends DelegatingFile<NormalizedNameFolder> {
private final Form displayForm;
public NormalizedNameFile(NormalizedNameFolder parent, File delegate, Form displayForm) {
super(parent, delegate);
this.displayForm = displayForm;
}
@Override
public String name() throws UncheckedIOException {
return Normalizer.normalize(super.name(), displayForm);
}
}

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.filesystem.charsets;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.delegating.DelegatingFileSystem;
public class NormalizedNameFileSystem extends NormalizedNameFolder implements DelegatingFileSystem {
public NormalizedNameFileSystem(Folder delegate, Form displayForm) {
super(null, delegate, displayForm);
}
@Override
public Folder getDelegate() {
return delegate;
}
}

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.filesystem.charsets;
import java.io.UncheckedIOException;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.delegating.DelegatingFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class NormalizedNameFolder extends DelegatingFolder<NormalizedNameFolder, NormalizedNameFile> {
private static final Logger LOG = LoggerFactory.getLogger(NormalizedNameFolder.class);
private final Form displayForm;
public NormalizedNameFolder(NormalizedNameFolder parent, Folder delegate, Form displayForm) {
super(parent, delegate);
this.displayForm = displayForm;
}
@Override
public String name() throws UncheckedIOException {
return Normalizer.normalize(super.name(), displayForm);
}
@Override
public NormalizedNameFile file(String name) throws UncheckedIOException {
String nfcName = Normalizer.normalize(name, Form.NFC);
String nfdName = Normalizer.normalize(name, Form.NFD);
NormalizedNameFile nfcFile = super.file(nfcName);
NormalizedNameFile nfdFile = super.file(nfdName);
if (!nfcName.equals(nfdName) && nfcFile.exists() && nfdFile.exists()) {
LOG.warn("Ambiguous file names \"" + nfcName + "\" (NFC) vs. \"" + nfdName + "\" (NFD). Both files exist. Using \"" + nfcName + "\" (NFC).");
} else if (!nfcName.equals(nfdName) && !nfcFile.exists() && nfdFile.exists()) {
LOG.info("Moving file from \"" + nfcName + "\" (NFD) to \"" + nfdName + "\" (NFC).");
nfdFile.moveTo(nfcFile);
}
return nfcFile;
}
@Override
protected NormalizedNameFile newFile(File delegate) {
return new NormalizedNameFile(this, delegate, displayForm);
}
@Override
public NormalizedNameFolder folder(String name) throws UncheckedIOException {
String nfcName = Normalizer.normalize(name, Form.NFC);
String nfdName = Normalizer.normalize(name, Form.NFD);
NormalizedNameFolder nfcFolder = super.folder(nfcName);
NormalizedNameFolder nfdFolder = super.folder(nfdName);
if (!nfcName.equals(nfdName) && nfcFolder.exists() && nfdFolder.exists()) {
LOG.warn("Ambiguous folder names \"" + nfcName + "\" (NFC) vs. \"" + nfdName + "\" (NFD). Both files exist. Using \"" + nfcName + "\" (NFC).");
} else if (!nfcName.equals(nfdName) && !nfcFolder.exists() && nfdFolder.exists()) {
LOG.info("Moving folder from \"" + nfcName + "\" (NFD) to \"" + nfdName + "\" (NFC).");
nfdFolder.moveTo(nfcFolder);
}
return nfcFolder;
}
@Override
protected NormalizedNameFolder newFolder(Folder delegate) {
return new NormalizedNameFolder(this, delegate, displayForm);
}
}

View File

@@ -0,0 +1,16 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
/**
* Makes sure, the filesystems wrapped by this filesystem work only on UTF-8 encoded file paths using Normalization Form C.
* Filesystems wrapping this file system, on the other hand, will get filenames reported in a specified Normalization Form.
* It is recommended to use NFD for OS X and NFC for other operating systems.
* When looking for a file or folder with a name given in either form, both possibilities are considered
* and files/folders stored in NFD are automatically migrated to NFC.
*/
package org.cryptomator.filesystem.charsets;

View File

@@ -0,0 +1,90 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.filesystem.charsets;
import java.nio.ByteBuffer;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.junit.Assert;
import org.junit.Test;
public class NormalizedNameFileSystemTest {
@Test
public void testFileMigration() {
FileSystem inMemoryFs = new InMemoryFileSystem();
try (WritableFile writable = inMemoryFs.file("\u006F\u0302").openWritable()) {
writable.write(ByteBuffer.allocate(0));
}
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFC);
Assert.assertTrue(normalizationFs.file("\u00F4").exists());
Assert.assertTrue(normalizationFs.file("\u006F\u0302").exists());
Assert.assertFalse(inMemoryFs.file("\u006F\u0302").exists());
Assert.assertTrue(inMemoryFs.file("\u00F4").exists());
}
@Test
public void testNoFileMigration() {
FileSystem inMemoryFs = new InMemoryFileSystem();
try (WritableFile writable = inMemoryFs.file("\u00F4").openWritable()) {
writable.write(ByteBuffer.allocate(0));
}
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFC);
Assert.assertTrue(normalizationFs.file("\u00F4").exists());
Assert.assertTrue(normalizationFs.file("\u006F\u0302").exists());
Assert.assertFalse(inMemoryFs.file("\u006F\u0302").exists());
Assert.assertTrue(inMemoryFs.file("\u00F4").exists());
}
@Test
public void testFolderMigration() {
FileSystem inMemoryFs = new InMemoryFileSystem();
inMemoryFs.folder("\u006F\u0302").create();
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFC);
Assert.assertTrue(normalizationFs.folder("\u00F4").exists());
Assert.assertTrue(normalizationFs.folder("\u006F\u0302").exists());
Assert.assertFalse(inMemoryFs.folder("\u006F\u0302").exists());
Assert.assertTrue(inMemoryFs.folder("\u00F4").exists());
}
@Test
public void testNoFolderMigration() {
FileSystem inMemoryFs = new InMemoryFileSystem();
inMemoryFs.folder("\u00F4").create();
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFC);
Assert.assertTrue(normalizationFs.folder("\u00F4").exists());
Assert.assertTrue(normalizationFs.folder("\u006F\u0302").exists());
Assert.assertFalse(inMemoryFs.folder("\u006F\u0302").exists());
Assert.assertTrue(inMemoryFs.folder("\u00F4").exists());
}
@Test
public void testNfcDisplayNames() {
FileSystem inMemoryFs = new InMemoryFileSystem();
inMemoryFs.folder("a\u00F4").create();
inMemoryFs.folder("b\u006F\u0302").create();
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFC);
Assert.assertEquals("a\u00F4", normalizationFs.folder("a\u00F4").name());
Assert.assertEquals("b\u00F4", normalizationFs.folder("b\u006F\u0302").name());
}
@Test
public void testNfdDisplayNames() {
FileSystem inMemoryFs = new InMemoryFileSystem();
inMemoryFs.folder("a\u00F4").create();
inMemoryFs.folder("b\u006F\u0302").create();
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFD);
Assert.assertEquals("a\u006F\u0302", normalizationFs.folder("a\u00F4").name());
Assert.assertEquals("b\u006F\u0302", normalizationFs.folder("b\u006F\u0302").name());
}
}

View File

@@ -0,0 +1,48 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.filesystem.charsets;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.File;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class NormalizedNameFileTest {
private File delegateNfc;
private File delegateNfd;
@Before
public void setup() {
delegateNfc = Mockito.mock(File.class);
delegateNfd = Mockito.mock(File.class);
Mockito.when(delegateNfc.name()).thenReturn("\u00C5");
Mockito.when(delegateNfd.name()).thenReturn("\u0041\u030A");
}
@Test
public void testDisplayNameInNfc() {
File file1 = new NormalizedNameFile(null, delegateNfc, Form.NFC);
File file2 = new NormalizedNameFile(null, delegateNfd, Form.NFC);
Assert.assertEquals("\u00C5", file1.name());
Assert.assertEquals("\u00C5", file2.name());
}
@Test
public void testDisplayNameInNfd() {
File file1 = new NormalizedNameFile(null, delegateNfc, Form.NFD);
File file2 = new NormalizedNameFile(null, delegateNfd, Form.NFD);
Assert.assertEquals("\u0041\u030A", file1.name());
Assert.assertEquals("\u0041\u030A", file2.name());
}
}

View File

@@ -0,0 +1,149 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.filesystem.charsets;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class NormalizedNameFolderTest {
private Folder delegate;
private File delegateSubFileNfc;
private File delegateSubFileNfd;
private Folder delegateSubFolderNfc;
private Folder delegateSubFolderNfd;
@Before
public void setup() {
delegate = Mockito.mock(Folder.class);
delegateSubFileNfc = Mockito.mock(File.class);
delegateSubFileNfd = Mockito.mock(File.class);
Mockito.when(delegate.file("\u00C5")).thenReturn(delegateSubFileNfc);
Mockito.when(delegateSubFileNfc.name()).thenReturn("\u00C5");
Mockito.when(delegate.file("\u0041\u030A")).thenReturn(delegateSubFileNfd);
Mockito.when(delegateSubFileNfd.name()).thenReturn("\u0041\u030A");
delegateSubFolderNfc = Mockito.mock(Folder.class);
delegateSubFolderNfd = Mockito.mock(Folder.class);
Mockito.when(delegate.folder("\u00F4")).thenReturn(delegateSubFolderNfc);
Mockito.when(delegateSubFolderNfc.name()).thenReturn("\u00F4");
Mockito.when(delegate.folder("\u006F\u0302")).thenReturn(delegateSubFolderNfd);
Mockito.when(delegateSubFolderNfd.name()).thenReturn("\u006F\u0302");
}
@Test
public void testDisplayNameInNfc() {
Folder folder1 = new NormalizedNameFolder(null, delegateSubFolderNfc, Form.NFC);
Folder folder2 = new NormalizedNameFolder(null, delegateSubFolderNfd, Form.NFC);
Assert.assertEquals("\u00F4", folder1.name());
Assert.assertEquals("\u00F4", folder2.name());
}
@Test
public void testDisplayNameInNfd() {
Folder folder1 = new NormalizedNameFolder(null, delegateSubFolderNfc, Form.NFD);
Folder folder2 = new NormalizedNameFolder(null, delegateSubFolderNfd, Form.NFD);
Assert.assertEquals("\u006F\u0302", folder1.name());
Assert.assertEquals("\u006F\u0302", folder2.name());
}
@Test
public void testNoFolderMigration1() {
Mockito.when(delegateSubFolderNfc.exists()).thenReturn(true);
Mockito.when(delegateSubFolderNfd.exists()).thenReturn(false);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
Folder sub1 = folder.folder("\u00F4");
Folder sub2 = folder.folder("\u006F\u0302");
Mockito.verify(delegateSubFolderNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testNoFolderMigration2() {
Mockito.when(delegateSubFolderNfc.exists()).thenReturn(true);
Mockito.when(delegateSubFolderNfd.exists()).thenReturn(true);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
Folder sub1 = folder.folder("\u00F4");
Folder sub2 = folder.folder("\u006F\u0302");
Mockito.verify(delegateSubFolderNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testNoFolderMigration3() {
Mockito.when(delegateSubFolderNfc.exists()).thenReturn(false);
Mockito.when(delegateSubFolderNfd.exists()).thenReturn(false);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
Folder sub1 = folder.folder("\u00F4");
Folder sub2 = folder.folder("\u006F\u0302");
Mockito.verify(delegateSubFolderNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testFolderMigration() {
Mockito.when(delegateSubFolderNfc.exists()).thenReturn(false);
Mockito.when(delegateSubFolderNfd.exists()).thenReturn(true);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
Folder sub1 = folder.folder("\u00F4");
Mockito.verify(delegateSubFolderNfd).moveTo(delegateSubFolderNfc);
Folder sub2 = folder.folder("\u006F\u0302");
Assert.assertSame(sub1, sub2);
}
@Test
public void testNoFileMigration1() {
Mockito.when(delegateSubFileNfc.exists()).thenReturn(true);
Mockito.when(delegateSubFileNfd.exists()).thenReturn(false);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
File sub1 = folder.file("\u00C5");
File sub2 = folder.file("\u0041\u030A");
Mockito.verify(delegateSubFileNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testNoFileMigration2() {
Mockito.when(delegateSubFileNfc.exists()).thenReturn(true);
Mockito.when(delegateSubFileNfd.exists()).thenReturn(true);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
File sub1 = folder.file("\u00C5");
File sub2 = folder.file("\u0041\u030A");
Mockito.verify(delegateSubFileNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testNoFileMigration3() {
Mockito.when(delegateSubFileNfc.exists()).thenReturn(false);
Mockito.when(delegateSubFileNfd.exists()).thenReturn(false);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
File sub1 = folder.file("\u00C5");
File sub2 = folder.file("\u0041\u030A");
Mockito.verify(delegateSubFileNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testFileMigration() {
Mockito.when(delegateSubFileNfc.exists()).thenReturn(false);
Mockito.when(delegateSubFileNfd.exists()).thenReturn(true);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
File sub1 = folder.file("\u00C5");
Mockito.verify(delegateSubFileNfd).moveTo(delegateSubFileNfc);
File sub2 = folder.file("\u0041\u030A");
Assert.assertSame(sub1, sub2);
}
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%16d %-5p [%c{1}:%L] %m%n" />
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="ACCEPT" />
</Console>
<Console name="StdErr" target="SYSTEM_ERR">
<PatternLayout pattern="%16d %-5p [%c{1}:%L] %m%n" />
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY" />
</Console>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="Console" />
<AppenderRef ref="StdErr" />
</Root>
</Loggers>
</Configuration>

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>filesystem-crypto-integration-tests</artifactId>
<name>Cryptomator filesystem: Encryption layer tests</name>

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>filesystem-crypto</artifactId>
<name>Cryptomator filesystem: Encryption layer</name>

View File

@@ -27,7 +27,12 @@ public class CryptoEngineModule {
@Provides
public SecureRandom provideSecureRandom() {
try {
return SecureRandom.getInstanceStrong();
// https://tersesystems.com/2015/12/17/the-right-way-to-use-securerandom/
final SecureRandom nativeRandom = SecureRandom.getInstanceStrong();
byte[] seed = nativeRandom.generateSeed(55); // NIST SP800-90A suggests 440 bits for SHA1 seed
SecureRandom sha1Random = SecureRandom.getInstance("SHA1PRNG");
sha1Random.setSeed(seed);
return sha1Random;
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("No strong PRNGs available.", e);
}

View File

@@ -23,6 +23,7 @@ import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.common.LazyInitializer;
import org.cryptomator.common.WeakValuedCache;
import org.cryptomator.common.streams.AutoClosingStream;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.filesystem.Deleter;
import org.cryptomator.filesystem.File;
@@ -74,13 +75,13 @@ class CryptoFolder extends CryptoNode implements Folder {
@Override
public Stream<? extends Node> children() {
return Stream.concat(files(), folders());
return AutoClosingStream.from(Stream.concat(files(), folders()));
}
@Override
public Stream<CryptoFile> files() {
assert forceGetPhysicalFolder().exists();
return forceGetPhysicalFolder().files().map(File::name).filter(isEncryptedFileName()).map(this::decryptChildFileName).map(this::file);
final Stream<? extends File> files = physicalFolder().filter(Folder::exists).map(Folder::files).orElse(Stream.empty());
return files.map(File::name).filter(isEncryptedFileName()).map(this::decryptChildFileName).map(this::file);
}
private Predicate<String> isEncryptedFileName() {
@@ -103,8 +104,8 @@ class CryptoFolder extends CryptoNode implements Folder {
@Override
public Stream<CryptoFolder> folders() {
assert forceGetPhysicalFolder().exists();
return forceGetPhysicalFolder().files().map(File::name).filter(isEncryptedDirectoryName()).map(this::decryptChildFolderName).map(this::folder);
final Stream<? extends File> files = physicalFolder().filter(Folder::exists).map(Folder::files).orElse(Stream.empty());
return files.map(File::name).filter(isEncryptedDirectoryName()).map(this::decryptChildFolderName).map(this::folder);
}
private Predicate<String> isEncryptedDirectoryName() {
@@ -189,7 +190,7 @@ class CryptoFolder extends CryptoNode implements Folder {
Folder physicalFolder = forceGetPhysicalFolder();
physicalFolder.delete();
Folder physicalFolderParent = physicalFolder.parent().get();
if (physicalFolderParent.folders().count() == 0) {
if (physicalFolderParent.exists() && physicalFolderParent.folders().count() == 0) {
physicalFolderParent.delete();
}
forceGetPhysicalFile().delete();

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>filesystem-inmemory</artifactId>
<name>Cryptomator filesystem: In-memory mock</name>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>filesystem-invariants-tests</artifactId>
<name>Cryptomator filesystem: Invariants tests</name>
@@ -20,6 +20,10 @@
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-api</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-charsets</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-crypto</artifactId>

View File

@@ -4,11 +4,13 @@ import static org.cryptomator.common.test.TempFilesRemovedOnShutdown.createTempD
import java.io.IOException;
import java.io.UncheckedIOException;
import java.text.Normalizer.Form;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.charsets.NormalizedNameFileSystem;
import org.cryptomator.filesystem.crypto.CryptoEngineTestModule;
import org.cryptomator.filesystem.crypto.CryptoFileSystemDelegate;
import org.cryptomator.filesystem.crypto.CryptoFileSystemTestComponent;
@@ -35,8 +37,10 @@ class FileSystemFactories implements Iterable<FileSystemFactory> {
add("ShorteningFileSystem > InMemoryFileSystem", this::createShorteningFileSystemInMemory);
add("StatsFileSystem > NioFileSystem", this::createStatsFileSystemNio);
add("StatsFileSystem > InMemoryFileSystem", this::createStatsFileSystemInMemory);
add("StatsFileSystem > CryptoFileSystem > ShorteningFileSystem > InMemoryFileSystem", this::createCompoundFileSystemInMemory);
add("StatsFileSystem > CryptoFileSystem > ShorteningFileSystem > NioFileSystem", this::createCompoundFileSystemNio);
add("NormalizingFileSystem > NioFileSystem", this::createNormalizingFileSystemNio);
add("NormalizingFileSystem > InMemoryFileSystem", this::createNormalizingFileSystemInMemory);
add("StatsFileSystem > NormalizingFileSystem > CryptoFileSystem > ShorteningFileSystem > InMemoryFileSystem", this::createCompoundFileSystemInMemory);
add("StatsFileSystem > NormalizingFileSystem > CryptoFileSystem > ShorteningFileSystem > NioFileSystem", this::createCompoundFileSystemNio);
}
private FileSystem createCryptoFileSystemInMemory() {
@@ -63,6 +67,14 @@ class FileSystemFactories implements Iterable<FileSystemFactory> {
return createStatsFileSystem(createInMemoryFileSystem());
}
private FileSystem createNormalizingFileSystemNio() {
return createNormalizingFileSystem(createInMemoryFileSystem());
}
private FileSystem createNormalizingFileSystemInMemory() {
return createNormalizingFileSystem(createInMemoryFileSystem());
}
private FileSystem createCompoundFileSystemNio() {
return createCompoundFileSystem(createNioFileSystem());
}
@@ -84,13 +96,17 @@ class FileSystemFactories implements Iterable<FileSystemFactory> {
}
private FileSystem createCompoundFileSystem(FileSystem delegate) {
return createStatsFileSystem(createCryptoFileSystem(createShorteningFileSystem(delegate)));
return createStatsFileSystem(createNormalizingFileSystem(createCryptoFileSystem(createShorteningFileSystem(delegate))));
}
private FileSystem createStatsFileSystem(FileSystem delegate) {
return new StatsFileSystem(delegate);
}
private FileSystem createNormalizingFileSystem(FileSystem delegate) {
return new NormalizedNameFileSystem(delegate, Form.NFC);
}
private FileSystem createCryptoFileSystem(FileSystem delegate) {
CRYPTO_FS_COMP.cryptoFileSystemFactory().initializeNew(delegate, "aPassphrase");
return CRYPTO_FS_COMP.cryptoFileSystemFactory().unlockExisting(delegate, "aPassphrase", Mockito.mock(CryptoFileSystemDelegate.class));

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>filesystem-nameshortening</artifactId>
<name>Cryptomator filesystem: Name shortening layer</name>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>filesystem-nio</artifactId>
<name>Cryptomator filesystem: NIO-based physical layer</name>

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>filesystem-stats</artifactId>
<name>Cryptomator filesystem: Throughput statistics</name>

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>frontend-api</artifactId>
<name>Cryptomator frontend: API</name>

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>frontend-webdav</artifactId>
<name>Cryptomator frontend: WebDAV frontend</name>

View File

@@ -10,10 +10,12 @@ package org.cryptomator.frontend.webdav;
import javax.inject.Singleton;
import org.cryptomator.common.CommonsModule;
import dagger.Component;
@Singleton
@Component
@Component(modules = {CommonsModule.class})
public interface WebDavComponent {
WebDavServer server();

View File

@@ -32,6 +32,7 @@ import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
import org.apache.jackrabbit.webdav.property.ResourceType;
import org.cryptomator.common.streams.AutoClosingStream;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
import org.cryptomator.filesystem.WritableFile;
@@ -90,7 +91,8 @@ class DavFolder extends DavNode<FolderLocator> {
public DavResourceIterator getMembers() {
final Stream<DavFolder> folders = node.folders().map(this::folderToDavFolder);
final Stream<DavFile> files = node.files().map(this::fileToDavFile);
return new DavResourceIteratorImpl(Stream.concat(folders, files).collect(Collectors.toList()));
final Stream<DavResource> members = AutoClosingStream.from(Stream.concat(folders, files));
return new DavResourceIteratorImpl(members.collect(Collectors.toList()));
}
private DavFolder folderToDavFolder(FolderLocator memberFolder) {
@@ -115,9 +117,15 @@ class DavFolder extends DavNode<FolderLocator> {
* Error 404 if no child with the given name exists
*/
private Node getMemberNode(String name) throws DavException {
return node.children().filter(c -> c.name().equals(name)).findAny().orElseThrow(() -> {
return new DavException(DavServletResponse.SC_NOT_FOUND, "No such file or directory: " + node.getResourcePath() + name);
});
Node file = node.file(name);
Node folder = node.folder(name);
if (file.exists()) {
return file;
} else if (folder.exists()) {
return folder;
} else {
throw new DavException(DavServletResponse.SC_NOT_FOUND, "No such file or directory: " + node.getResourcePath() + name);
}
}
@Override

View File

@@ -12,11 +12,13 @@ package org.cryptomator.frontend.webdav.mount;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Comparator;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.commons.io.IOUtils;
@@ -26,15 +28,18 @@ import org.cryptomator.frontend.CommandFailedException;
import org.cryptomator.frontend.Frontend.MountParam;
@Singleton
final class MacOsXWebDavMounter implements WebDavMounterStrategy {
final class MacOsXAppleScriptWebDavMounter implements WebDavMounterStrategy {
private final Comparator<String> semVerComparator;
@Inject
MacOsXWebDavMounter() {
MacOsXAppleScriptWebDavMounter(@Named("SemVer") Comparator<String> semVerComparator) {
this.semVerComparator = semVerComparator;
}
@Override
public boolean shouldWork() {
return SystemUtils.IS_OS_MAC_OSX;
return SystemUtils.IS_OS_MAC_OSX && semVerComparator.compare(SystemUtils.OS_VERSION, "10.10") >= 0;
}
@Override

View File

@@ -0,0 +1,89 @@
/*******************************************************************************
* Copyright (c) 2014, 2016 Sebastian Stenzel, Markus Kreusch
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation, strategy fine tuning
* Markus Kreusch - Refactored WebDavMounter to use strategy pattern
******************************************************************************/
package org.cryptomator.frontend.webdav.mount;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Comparator;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.frontend.CommandFailedException;
import org.cryptomator.frontend.Frontend.MountParam;
import org.cryptomator.frontend.webdav.mount.command.Script;
@Singleton
final class MacOsXShellScriptWebDavMounter implements WebDavMounterStrategy {
private final Comparator<String> semVerComparator;
@Inject
MacOsXShellScriptWebDavMounter(@Named("SemVer") Comparator<String> semVerComparator) {
this.semVerComparator = semVerComparator;
}
@Override
public boolean shouldWork() {
return SystemUtils.IS_OS_MAC_OSX && semVerComparator.compare(SystemUtils.OS_VERSION, "10.10") < 0;
}
@Override
public void warmUp(int serverPort) {
// no-op
}
@Override
public WebDavMount mount(URI uri, Map<MountParam, Optional<String>> mountParams) throws CommandFailedException {
final String mountName = mountParams.getOrDefault(MountParam.MOUNT_NAME, Optional.empty()).orElseThrow(() -> {
return new IllegalArgumentException("Missing mount parameter MOUNT_NAME.");
});
// we don't use the uri to derive a path, as it *could* be longer than 255 chars.
final String path = "/Volumes/Cryptomator_" + UUID.randomUUID().toString();
final Script mountScript = Script.fromLines("mkdir \"$MOUNT_PATH\"", "mount_webdav -S -v $MOUNT_NAME \"$DAV_AUTHORITY$DAV_PATH\" \"$MOUNT_PATH\"").addEnv("DAV_AUTHORITY", uri.getRawAuthority())
.addEnv("DAV_PATH", uri.getRawPath()).addEnv("MOUNT_PATH", path).addEnv("MOUNT_NAME", mountName);
mountScript.execute();
return new MacWebDavMount(path);
}
private static class MacWebDavMount extends AbstractWebDavMount {
private final String mountPath;
private final Script revealScript;
private final Script unmountScript;
private MacWebDavMount(String mountPath) {
this.mountPath = mountPath;
this.revealScript = Script.fromLines("open \"$MOUNT_PATH\"").addEnv("MOUNT_PATH", mountPath);
this.unmountScript = Script.fromLines("diskutil umount $MOUNT_PATH").addEnv("MOUNT_PATH", mountPath);
}
@Override
public void unmount() throws CommandFailedException {
// only attempt unmount if user didn't unmount manually:
if (Files.exists(FileSystems.getDefault().getPath(mountPath))) {
unmountScript.execute();
}
}
@Override
public void reveal() throws CommandFailedException {
revealScript.execute();
}
}
}

View File

@@ -19,74 +19,87 @@ import javax.inject.Singleton;
@Singleton
class MountStrategies implements Collection<WebDavMounterStrategy> {
private final Collection<WebDavMounterStrategy> delegate;
@Inject
MountStrategies(LinuxGvfsWebDavMounter linuxMounter, MacOsXWebDavMounter osxMounter, WindowsWebDavMounter winMounter) {
delegate = unmodifiableList(asList(linuxMounter, osxMounter, winMounter));
MountStrategies(LinuxGvfsWebDavMounter linuxMounter, MacOsXAppleScriptWebDavMounter osxAppleScriptMounter, MacOsXShellScriptWebDavMounter osxShellScriptMounter, WindowsWebDavMounter winMounter) {
delegate = unmodifiableList(asList(linuxMounter, osxAppleScriptMounter, osxShellScriptMounter, winMounter));
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean contains(Object o) {
return delegate.contains(o);
}
@Override
public Iterator<WebDavMounterStrategy> iterator() {
return delegate.iterator();
}
@Override
public Object[] toArray() {
return delegate.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return delegate.toArray(a);
}
@Override
public boolean add(WebDavMounterStrategy e) {
return delegate.add(e);
}
@Override
public boolean remove(Object o) {
return delegate.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return delegate.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends WebDavMounterStrategy> c) {
return delegate.addAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
return delegate.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return delegate.retainAll(c);
}
@Override
public void clear() {
delegate.clear();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
}

View File

@@ -11,10 +11,16 @@ package org.cryptomator.frontend.webdav.mount;
import static org.cryptomator.frontend.webdav.mount.command.Script.fromLines;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -22,12 +28,16 @@ import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.frontend.CommandFailedException;
import org.cryptomator.frontend.Frontend.MountParam;
import org.cryptomator.frontend.webdav.mount.command.CommandResult;
import org.cryptomator.frontend.webdav.mount.command.Script;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A {@link WebDavMounterStrategy} utilizing the "net use" command.
@@ -37,7 +47,9 @@ import org.cryptomator.frontend.webdav.mount.command.Script;
@Singleton
final class WindowsWebDavMounter implements WebDavMounterStrategy {
private static final Logger LOG = LoggerFactory.getLogger(WindowsWebDavMounter.class);
private static final Pattern WIN_MOUNT_DRIVELETTER_PATTERN = Pattern.compile("\\s*([A-Z]):\\s*");
private static final Pattern REG_QUERY_PROXY_OVERRIDES_PATTERN = Pattern.compile("\\s*ProxyOverride\\s+REG_SZ\\s+(.*)\\s*");
private static final String AUTO_ASSIGN_DRIVE_LETTER = "*";
private static final String LOCALHOST = "localhost";
private static final int MOUNT_TIMEOUT_SECONDS = 60;
@@ -60,12 +72,12 @@ final class WindowsWebDavMounter implements WebDavMounterStrategy {
@Override
public WebDavMount mount(URI uri, Map<MountParam, Optional<String>> mountParams) throws CommandFailedException {
final String driveLetter = mountParams.getOrDefault(MountParam.WIN_DRIVE_LETTER, Optional.of(AUTO_ASSIGN_DRIVE_LETTER)).orElse(AUTO_ASSIGN_DRIVE_LETTER);
final String driveLetter = mountParams.getOrDefault(MountParam.WIN_DRIVE_LETTER, Optional.empty()).orElse(AUTO_ASSIGN_DRIVE_LETTER);
if (driveLetters.getOccupiedDriveLetters().contains(CharUtils.toChar(driveLetter))) {
throw new CommandFailedException("Drive letter occupied.");
}
final String hostname = mountParams.getOrDefault(MountParam.HOSTNAME, Optional.of(LOCALHOST)).orElse(LOCALHOST);
final String hostname = mountParams.getOrDefault(MountParam.HOSTNAME, Optional.empty()).orElse(LOCALHOST);
try {
final URI adjustedUri = new URI(uri.getScheme(), uri.getUserInfo(), hostname, uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment());
CommandResult mountResult = mount(adjustedUri, driveLetter);
@@ -74,14 +86,14 @@ final class WindowsWebDavMounter implements WebDavMounterStrategy {
throw new IllegalArgumentException("Invalid host: " + hostname);
}
}
private CommandResult mount(URI uri, String driveLetter) throws CommandFailedException {
final Script proxyBypassScript = fromLines(
"reg add \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\" /v \"ProxyOverride\" /d \"<local>;%DAV_HOST%;%DAV_HOST%:%DAV_PORT%\" /f");
proxyBypassScript.addEnv("DAV_HOST", uri.getHost());
proxyBypassScript.addEnv("DAV_PORT", String.valueOf(uri.getPort()));
proxyBypassScript.execute();
try {
addProxyOverrides(uri);
} catch (IOException e) {
throw new CommandFailedException(e);
}
final String driveLetterStr = AUTO_ASSIGN_DRIVE_LETTER.equals(driveLetter) ? AUTO_ASSIGN_DRIVE_LETTER : driveLetter + ":";
final Script mountScript = fromLines("net use %DRIVE_LETTER% \\\\%DAV_HOST%@%DAV_PORT%\\DavWWWRoot%DAV_UNC_PATH% /persistent:no");
mountScript.addEnv("DRIVE_LETTER", driveLetterStr);
@@ -90,6 +102,44 @@ final class WindowsWebDavMounter implements WebDavMounterStrategy {
mountScript.addEnv("DAV_UNC_PATH", uri.getRawPath().replace('/', '\\'));
return mountScript.execute(MOUNT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
private void addProxyOverrides(URI uri) throws IOException, CommandFailedException {
try {
// get existing value for ProxyOverride key from reqistry:
ProcessBuilder query = new ProcessBuilder("reg", "query", "\"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\"", "/v", "ProxyOverride");
Process queryCmd = query.start();
String queryStdOut = IOUtils.toString(queryCmd.getInputStream(), StandardCharsets.UTF_8);
int queryResult = queryCmd.waitFor();
// determine new value for ProxyOverride key:
Set<String> overrides = new HashSet<>();
Matcher matcher = REG_QUERY_PROXY_OVERRIDES_PATTERN.matcher(queryStdOut);
if (queryResult == 0 && matcher.find()) {
String[] existingOverrides = StringUtils.split(matcher.group(1), ';');
overrides.addAll(Arrays.asList(existingOverrides));
}
overrides.removeIf(s -> s.startsWith(uri.getHost() + ":"));
overrides.add("<local>");
overrides.add(uri.getHost());
overrides.add(uri.getHost() + ":" + uri.getPort());
// set new value:
String overridesStr = StringUtils.join(overrides, ';');
ProcessBuilder add = new ProcessBuilder("reg", "add", "\"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\"", "/v", "ProxyOverride", "/d", "\"" + overridesStr + "\"", "/f");
LOG.debug("Invoking command: " + StringUtils.join(add.command(), ' '));
Process addCmd = add.start();
int addResult = addCmd.waitFor();
if (addResult != 0) {
String addStdErr = IOUtils.toString(addCmd.getErrorStream(), StandardCharsets.UTF_8);
throw new CommandFailedException(addStdErr);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
InterruptedIOException ioException = new InterruptedIOException();
ioException.initCause(e);
throw ioException;
}
}
private String getDriveLetter(String result) throws CommandFailedException {
final Matcher matcher = WIN_MOUNT_DRIVELETTER_PATTERN.matcher(result);

1
main/jacoco-report/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target/

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2016 Sebastian Stenzel This file is licensed under the terms of the MIT license. See the LICENSE.txt file for more info. -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.1.0-SNAPSHOT</version>
</parent>
<artifactId>jacoco-report</artifactId>
<name>Cryptomator Code Coverage Report</name>
<dependencies>
<!-- Commons -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>commons</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>commons-test</artifactId>
</dependency>
<!-- Filesystem Layers -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-api</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-crypto</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-crypto-integration-tests</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-inmemory</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-nameshortening</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-nio</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-stats</artifactId>
</dependency>
<!-- Frontends -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>frontend-api</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>frontend-webdav</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>report-aggregate</id>
<phase>verify</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -7,7 +7,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
<packaging>pom</packaging>
<name>Cryptomator</name>
@@ -35,12 +35,12 @@
<hamcrest.version>1.3</hamcrest.version> <!-- keep in sync with version required by JUnit -->
<commons-io.version>2.4</commons-io.version>
<commons-collections.version>4.0</commons-collections.version>
<commons-lang3.version>3.3.2</commons-lang3.version>
<commons-lang3.version>3.4</commons-lang3.version>
<commons-codec.version>1.10</commons-codec.version>
<commons-httpclient.version>3.1</commons-httpclient.version>
<jackson-databind.version>2.4.4</jackson-databind.version>
<mockito.version>1.10.19</mockito.version>
<dagger.version>2.0.2</dagger.version>
<dagger.version>2.4</dagger.version>
</properties>
<repositories>
@@ -49,6 +49,16 @@
<url>https://jitpack.io</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>jacoco-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<dependencyManagement>
<dependencies>
@@ -70,6 +80,11 @@
<artifactId>filesystem-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-charsets</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-nio</artifactId>
@@ -81,6 +96,12 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-invariants-tests</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-nameshortening</artifactId>
@@ -254,14 +275,6 @@
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jul</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
</dependencies>
<modules>
@@ -278,13 +291,21 @@
<module>frontend-api</module>
<module>frontend-webdav</module>
<module>ui</module>
<module>filesystem-charsets</module>
</modules>
<profiles>
<profile>
<id>uber-jar</id>
<id>release</id>
<modules>
<module>uber-jar</module>
<module>ant-kit</module>
</modules>
</profile>
<profile>
<id>test-coverage</id>
<modules>
<module>jacoco-report</module>
</modules>
</profile>
</profiles>
@@ -311,7 +332,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.5.201505241946</version>
<version>0.7.7-SNAPSHOT</version>
<executions>
<execution>
<id>prepare-agent</id>
@@ -320,6 +341,12 @@
</goals>
</execution>
</executions>
<configuration>
<excludes>
<exclude>**/*_*</exclude>
<exclude>**/Dagger*</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</pluginManagement>
@@ -338,6 +365,9 @@
<artifactId>coveralls-maven-plugin</artifactId>
<version>4.0.0</version>
<configuration>
<jacocoReports>
<jacocoReport>jacoco-report/target/site/jacoco-aggregate/jacoco.xml</jacocoReport>
</jacocoReports>
<repoToken>${env.COVERALLS_REPO_TOKEN}</repoToken>
</configuration>
</plugin>

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>uber-jar</artifactId>
<packaging>pom</packaging>

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
</parent>
<artifactId>ui</artifactId>
<name>Cryptomator GUI</name>
@@ -38,6 +38,10 @@
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-crypto</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-charsets</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-stats</artifactId>

View File

@@ -36,6 +36,8 @@ public class Cryptomator {
private static final CleanShutdownPerformer CLEAN_SHUTDOWN_PERFORMER = new CleanShutdownPerformer();
public static void main(String[] args) {
String cryptomatorVersion = Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion()).orElse("SNAPSHOT");
LOG.info("Starting Cryptomator {} on {} {} ({})", cryptomatorVersion, SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
if (SystemUtils.IS_OS_MAC_OSX) {
/*
* On OSX we're in an awkward position. We need to register a handler in the main thread of this application. However, we can't

View File

@@ -29,5 +29,5 @@ interface CryptomatorComponent {
Localization localization();
TrayIconUtil trayIconUtil();
ExitUtil exitUtil();
}

View File

@@ -8,13 +8,13 @@
*******************************************************************************/
package org.cryptomator.ui;
import java.util.Comparator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.inject.Named;
import javax.inject.Singleton;
import org.cryptomator.common.CommonsModule;
import org.cryptomator.crypto.engine.impl.CryptoEngineModule;
import org.cryptomator.frontend.FrontendFactory;
import org.cryptomator.frontend.webdav.WebDavServer;
@@ -24,7 +24,6 @@ import org.cryptomator.ui.model.VaultObjectMapperProvider;
import org.cryptomator.ui.settings.Settings;
import org.cryptomator.ui.settings.SettingsProvider;
import org.cryptomator.ui.util.DeferredCloser;
import org.cryptomator.ui.util.SemVerComparator;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -33,7 +32,7 @@ import dagger.Provides;
import javafx.application.Application;
import javafx.stage.Stage;
@Module(includes = CryptoEngineModule.class)
@Module(includes = {CryptoEngineModule.class, CommonsModule.class})
class CryptomatorModule {
private final Application application;
@@ -65,13 +64,6 @@ class CryptomatorModule {
return closer;
}
@Provides
@Singleton
@Named("SemVer")
Comparator<String> provideSemVerComparator() {
return new SemVerComparator();
}
@Provides
@Singleton
@Named("VaultJsonMapper")

View File

@@ -38,22 +38,41 @@ import javafx.application.Platform;
import javafx.stage.Stage;
@Singleton
class TrayIconUtil {
class ExitUtil {
private static final Logger LOG = LoggerFactory.getLogger(TrayIconUtil.class);
private static final Logger LOG = LoggerFactory.getLogger(ExitUtil.class);
private final Stage mainWindow;
private final Localization localization;
private final Settings settings;
@Inject
public TrayIconUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings) {
public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings) {
this.mainWindow = mainWindow;
this.localization = localization;
this.settings = settings;
}
public void initTrayIcon(Runnable exitCommand) {
public void initExitHandler(Runnable exitCommand) {
if (SystemUtils.IS_OS_LINUX) {
initMinimizeExitHandler(exitCommand);
} else {
initTrayIconExitHandler(exitCommand);
}
}
private void initMinimizeExitHandler(Runnable exitCommand) {
mainWindow.setOnCloseRequest(e -> {
if (Platform.isImplicitExit()) {
exitCommand.run();
} else {
mainWindow.setIconified(true);
e.consume();
}
});
}
private void initTrayIconExitHandler(Runnable exitCommand) {
final TrayIcon trayIcon = createTrayIcon(exitCommand);
try {
SystemTray.getSystemTray().add(trayIcon);
@@ -117,6 +136,7 @@ class TrayIconUtil {
return;
} else {
settings.setNumTrayNotifications(settings.getNumTrayNotifications() - 1);
settings.save();
}
final Runnable notificationCmd;
if (SystemUtils.IS_OS_MAC_OSX) {

View File

@@ -38,6 +38,7 @@ public class MainApplication extends Application {
@Override
public void start(Stage primaryStage) throws IOException {
LOG.info("JavaFX application started");
final CryptomatorComponent comp = DaggerCryptomatorComponent.builder().cryptomatorModule(new CryptomatorModule(this, primaryStage)).build();
final MainController mainCtrl = comp.mainController();
closer = comp.deferredCloser();
@@ -67,7 +68,7 @@ public class MainApplication extends Application {
// show window and start observing its focus:
primaryStage.show();
ActiveWindowStyleSupport.startObservingFocus(primaryStage);
comp.trayIconUtil().initTrayIcon(this::quit);
comp.exitUtil().initExitHandler(this::quit);
// open files, if requested during startup:
for (String arg : getParameters().getUnnamed()) {

View File

@@ -12,7 +12,6 @@ import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.util.Optional;
import java.util.ResourceBundle;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -37,19 +36,18 @@ import javafx.scene.control.Hyperlink;
import javafx.scene.text.Text;
@Singleton
public class ChangePasswordController extends AbstractFXMLViewController {
public class ChangePasswordController extends LocalizedFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(ChangePasswordController.class);
private final Application app;
private final Localization localization;
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
private Optional<ChangePasswordListener> listener = Optional.empty();
@Inject
public ChangePasswordController(Application app, Localization localization) {
super(localization);
this.app = app;
this.localization = localization;
}
@FXML
@@ -83,11 +81,6 @@ public class ChangePasswordController extends AbstractFXMLViewController {
return getClass().getResource("/fxml/change_password.fxml");
}
@Override
protected ResourceBundle getFxmlResourceBundle() {
return localization;
}
// ****************************************
// Downloads link
// ****************************************
@@ -143,13 +136,13 @@ public class ChangePasswordController extends AbstractFXMLViewController {
private void invokeListenerLater(ChangePasswordListener listener) {
Platform.runLater(() -> {
listener.didChangePassword(this);
listener.didChangePassword();
});
}
@FunctionalInterface
interface ChangePasswordListener {
void didChangePassword(ChangePasswordController ctrl);
void didChangePassword();
}
}

View File

@@ -13,7 +13,6 @@ import java.io.UncheckedIOException;
import java.net.URL;
import java.nio.file.FileAlreadyExistsException;
import java.util.Optional;
import java.util.ResourceBundle;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -34,17 +33,16 @@ import javafx.scene.control.Button;
import javafx.scene.control.Label;
@Singleton
public class InitializeController extends AbstractFXMLViewController {
public class InitializeController extends LocalizedFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class);
private final Localization localization;
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
private Optional<InitializationListener> listener = Optional.empty();
@Inject
public InitializeController(Localization localization) {
this.localization = localization;
super(localization);
}
@FXML
@@ -71,11 +69,6 @@ public class InitializeController extends AbstractFXMLViewController {
return getClass().getResource("/fxml/initialize.fxml");
}
@Override
protected ResourceBundle getFxmlResourceBundle() {
return localization;
}
// ****************************************
// OK button
// ****************************************
@@ -111,13 +104,13 @@ public class InitializeController extends AbstractFXMLViewController {
private void invokeListenerLater(InitializationListener listener) {
Platform.runLater(() -> {
listener.didInitialize(this);
listener.didInitialize();
});
}
@FunctionalInterface
interface InitializationListener {
void didInitialize(InitializeController ctrl);
void didInitialize();
}
}

View File

@@ -0,0 +1,18 @@
package org.cryptomator.ui.controllers;
import org.cryptomator.ui.settings.Localization;
abstract class LocalizedFXMLViewController extends AbstractFXMLViewController {
protected final Localization localization;
public LocalizedFXMLViewController(Localization localization) {
this.localization = localization;
}
@Override
protected Localization getFxmlResourceBundle() {
return localization;
}
}

View File

@@ -9,7 +9,6 @@
package org.cryptomator.ui.controllers;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -39,10 +38,9 @@ import javafx.scene.control.cell.CheckBoxListCell;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class MacWarningsController extends AbstractFXMLViewController {
public class MacWarningsController extends LocalizedFXMLViewController {
private final Application application;
private final Localization localization;
private final ObservableList<Warning> warnings = FXCollections.observableArrayList();
private final ListChangeListener<String> unauthenticatedResourcesChangeListener = this::unauthenticatedResourcesDidChange;
private final ChangeListener<Boolean> stageVisibilityChangeListener = this::windowVisibilityDidChange;
@@ -51,8 +49,8 @@ public class MacWarningsController extends AbstractFXMLViewController {
@Inject
public MacWarningsController(Application application, Localization localization) {
super(localization);
this.application = application;
this.localization = localization;
}
@FXML
@@ -85,11 +83,6 @@ public class MacWarningsController extends AbstractFXMLViewController {
return getClass().getResource("/fxml/mac_warnings.fxml");
}
@Override
protected ResourceBundle getFxmlResourceBundle() {
return localization;
}
@Override
public void initStage(Stage stage) {
super.initStage(stage);
@@ -130,7 +123,7 @@ public class MacWarningsController extends AbstractFXMLViewController {
private void windowVisibilityDidChange(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (Boolean.TRUE.equals(newValue)) {
stage.setTitle(String.format(localization.getString("macWarnings.windowTitle"), vault.get().getName()));
stage.setTitle(String.format(localization.getString("macWarnings.windowTitle"), vault.get().name().getValue()));
warnings.addAll(vault.get().getNamesOfResourcesWithInvalidMac().stream().map(Warning::new).collect(Collectors.toList()));
vault.get().getNamesOfResourcesWithInvalidMac().addListener(this.unauthenticatedResourcesChangeListener);
} else {

View File

@@ -16,7 +16,6 @@ import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javax.inject.Inject;
import javax.inject.Named;
@@ -41,6 +40,7 @@ import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
@@ -57,15 +57,16 @@ import javafx.stage.FileChooser;
import javafx.stage.Stage;
@Singleton
public class MainController extends AbstractFXMLViewController {
public class MainController extends LocalizedFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(MainController.class);
private final Stage mainWindow;
private final Localization localization;
private final VaultFactory vaultFactoy;
private final Lazy<WelcomeController> welcomeController;
private final Lazy<InitializeController> initializeController;
private final Lazy<NotFoundController> notFoundController;
private final Lazy<UpgradeController> upgradeController;
private final Lazy<UnlockController> unlockController;
private final Provider<UnlockedController> unlockedControllerProvider;
private final Lazy<ChangePasswordController> changePasswordController;
@@ -73,25 +74,30 @@ public class MainController extends AbstractFXMLViewController {
private final ObjectProperty<AbstractFXMLViewController> activeController = new SimpleObjectProperty<>();
private final ObservableList<Vault> vaults;
private final ObjectProperty<Vault> selectedVault = new SimpleObjectProperty<>();
private final MonadicBinding<Boolean> isSelectedVaultUnlocked = EasyBind.select(selectedVault).selectObject(Vault::unlockedProperty);
private final MonadicBinding<Boolean> isSelectedVaultUnlocked = EasyBind.select(selectedVault).selectObject(Vault::unlockedProperty);;
private final Binding<Boolean> canEditSelectedVault = EasyBind.combine(selectedVault.isNull(), isSelectedVaultUnlocked.orElse(false), Boolean::logicalOr);
private final BooleanBinding isShowingSettings;
private final Map<Vault, UnlockedController> unlockedVaults = new HashMap<>();
@Inject
public MainController(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, VaultFactory vaultFactoy, Lazy<WelcomeController> welcomeController,
Lazy<InitializeController> initializeController, Lazy<UnlockController> unlockController, Provider<UnlockedController> unlockedControllerProvider, Lazy<ChangePasswordController> changePasswordController,
Lazy<SettingsController> settingsController) {
Lazy<InitializeController> initializeController, Lazy<NotFoundController> notFoundController, Lazy<UpgradeController> upgradeController, Lazy<UnlockController> unlockController,
Provider<UnlockedController> unlockedControllerProvider, Lazy<ChangePasswordController> changePasswordController, Lazy<SettingsController> settingsController) {
super(localization);
this.mainWindow = mainWindow;
this.localization = localization;
this.vaultFactoy = vaultFactoy;
this.welcomeController = welcomeController;
this.initializeController = initializeController;
this.notFoundController = notFoundController;
this.upgradeController = upgradeController;
this.unlockController = unlockController;
this.unlockedControllerProvider = unlockedControllerProvider;
this.changePasswordController = changePasswordController;
this.settingsController = settingsController;
this.vaults = FXCollections.observableList(settings.getDirectories());
this.vaults.addListener((Change<? extends Vault> c) -> {
settings.save();
});
// derived bindings:
this.isShowingSettings = activeController.isEqualTo(settingsController.get());
@@ -133,9 +139,8 @@ public class MainController extends AbstractFXMLViewController {
removeVaultButton.disableProperty().bind(canEditSelectedVault);
emptyListInstructions.visibleProperty().bind(Bindings.isEmpty(vaults));
EasyBind.subscribe(activeController, this::activeControllerDidChange);
EasyBind.subscribe(selectedVault, this::selectedVaultDidChange);
EasyBind.subscribe(isSelectedVaultUnlocked, this::selectedVaultUnlockedDidChange);
EasyBind.subscribe(activeController, this::activeControllerDidChange);
EasyBind.subscribe(isShowingSettings, settingsButton::setSelected);
EasyBind.subscribe(addVaultContextMenu.showingProperty(), addVaultButton::setSelected);
}
@@ -145,11 +150,6 @@ public class MainController extends AbstractFXMLViewController {
return getClass().getResource("/fxml/main.fxml");
}
@Override
protected ResourceBundle getFxmlResourceBundle() {
return localization;
}
private ListCell<Vault> createDirecoryListCell(ListView<Vault> param) {
final DirectoryListCell cell = new DirectoryListCell();
cell.setVaultContextMenu(vaultListCellContextMenu);
@@ -172,19 +172,12 @@ public class MainController extends AbstractFXMLViewController {
@FXML
private void didClickCreateNewVault(ActionEvent event) {
final FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator vault", "*" + Vault.VAULT_FILE_EXTENSION));
final File file = fileChooser.showSaveDialog(mainWindow);
if (file == null) {
return;
}
try {
final Path vaultDir;
// enforce .cryptomator file extension:
if (!file.getName().endsWith(Vault.VAULT_FILE_EXTENSION)) {
vaultDir = file.toPath().resolveSibling(file.getName() + Vault.VAULT_FILE_EXTENSION);
} else {
vaultDir = file.toPath();
}
final Path vaultDir = file.toPath();
if (!Files.exists(vaultDir)) {
Files.createDirectory(vaultDir);
}
@@ -272,6 +265,10 @@ public class MainController extends AbstractFXMLViewController {
}
if (newValue.isUnlocked()) {
this.showUnlockedView(newValue);
} else if (!newValue.doesVaultDirectoryExist()) {
this.showNotFoundView();
} else if (newValue.isValidVaultDirectory() && newValue.needsUpgrade()) {
this.showUpgradeView();
} else if (newValue.isValidVaultDirectory()) {
this.showUnlockView();
} else {
@@ -279,29 +276,23 @@ public class MainController extends AbstractFXMLViewController {
}
}
private void selectedVaultUnlockedDidChange(Boolean unlocked) {
if (unlocked == null) {
// no vault selected -> no-op
} else if (unlocked) {
Platform.setImplicitExit(false);
this.showUnlockedView(selectedVault.get());
} else {
this.showUnlockView();
}
}
// ****************************************
// Public Bindings
// ****************************************
public Binding<String> windowTitle() {
return EasyBind.monadic(selectedVault).map(Vault::getName).orElse(localization.getString("app.name"));
return EasyBind.monadic(selectedVault).flatMap(Vault::name).orElse(localization.getString("app.name"));
}
// ****************************************
// Subcontroller for right panel
// ****************************************
private void showNotFoundView() {
final NotFoundController ctrl = notFoundController.get();
activeController.set(ctrl);
}
private void showInitializeView() {
final InitializeController ctrl = initializeController.get();
ctrl.vault.bind(selectedVault);
@@ -309,16 +300,35 @@ public class MainController extends AbstractFXMLViewController {
activeController.set(ctrl);
}
public void didInitialize(InitializeController ctrl) {
public void didInitialize() {
showUnlockView();
}
private void showUpgradeView() {
final UpgradeController ctrl = upgradeController.get();
ctrl.vault.bind(selectedVault);
ctrl.setListener(this::didUpgrade);
activeController.set(ctrl);
}
public void didUpgrade() {
showUnlockView();
}
private void showUnlockView() {
final UnlockController ctrl = unlockController.get();
ctrl.vault.bind(selectedVault);
ctrl.setListener(this::didUnlock);
activeController.set(ctrl);
}
public void didUnlock(Vault vault) {
Platform.setImplicitExit(false);
if (vault.equals(selectedVault.getValue())) {
this.showUnlockedView(vault);
}
}
private void showUnlockedView(Vault vault) {
final UnlockedController ctrl = unlockedVaults.computeIfAbsent(vault, k -> {
return unlockedControllerProvider.get();
@@ -343,7 +353,7 @@ public class MainController extends AbstractFXMLViewController {
activeController.set(ctrl);
}
public void didChangePassword(ChangePasswordController ctrl) {
public void didChangePassword() {
showUnlockView();
}

View File

@@ -0,0 +1,23 @@
package org.cryptomator.ui.controllers;
import java.net.URL;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.cryptomator.ui.settings.Localization;
@Singleton
public class NotFoundController extends LocalizedFXMLViewController {
@Inject
public NotFoundController(Localization localization) {
super(localization);
}
@Override
protected URL getFxmlResourceUrl() {
return getClass().getResource("/fxml/notfound.fxml");
}
}

View File

@@ -10,7 +10,6 @@ package org.cryptomator.ui.controllers;
import java.net.URL;
import java.util.Optional;
import java.util.ResourceBundle;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -28,14 +27,13 @@ import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
@Singleton
public class SettingsController extends AbstractFXMLViewController {
public class SettingsController extends LocalizedFXMLViewController {
private final Localization localization;
private final Settings settings;
@Inject
public SettingsController(Localization localization, Settings settings) {
this.localization = localization;
super(localization);
this.settings = settings;
}
@@ -44,7 +42,10 @@ public class SettingsController extends AbstractFXMLViewController {
@FXML
private TextField portField;
@FXML
private Label useIpv6Label;
@FXML
private CheckBox useIpv6Checkbox;
@@ -57,13 +58,14 @@ public class SettingsController extends AbstractFXMLViewController {
checkForUpdatesCheckbox.setSelected(settings.isCheckForUpdatesEnabled() && !areUpdatesManagedExternally());
portField.setText(String.valueOf(settings.getPort()));
portField.addEventFilter(KeyEvent.KEY_TYPED, this::filterNumericKeyEvents);
useIpv6Checkbox.setDisable(!SystemUtils.IS_OS_WINDOWS);
useIpv6Label.setVisible(SystemUtils.IS_OS_WINDOWS);
useIpv6Checkbox.setVisible(SystemUtils.IS_OS_WINDOWS);
useIpv6Checkbox.setSelected(SystemUtils.IS_OS_WINDOWS && settings.shouldUseIpv6());
versionLabel.setText(String.format(localization.getString("settings.version.label"), applicationVersion().orElse("SNAPSHOT")));
EasyBind.subscribe(checkForUpdatesCheckbox.selectedProperty(), settings::setCheckForUpdatesEnabled);
EasyBind.subscribe(checkForUpdatesCheckbox.selectedProperty(), this::checkForUpdateDidChange);
EasyBind.subscribe(portField.textProperty(), this::portDidChange);
EasyBind.subscribe(useIpv6Checkbox.selectedProperty(), settings::setUseIpv6);
EasyBind.subscribe(useIpv6Checkbox.selectedProperty(), this::useIpv6DidChange);
}
@Override
@@ -71,30 +73,34 @@ public class SettingsController extends AbstractFXMLViewController {
return getClass().getResource("/fxml/settings.fxml");
}
@Override
protected ResourceBundle getFxmlResourceBundle() {
return localization;
}
private Optional<String> applicationVersion() {
return Optional.ofNullable(getClass().getPackage().getImplementationVersion());
}
private void checkForUpdateDidChange(Boolean newValue) {
settings.setCheckForUpdatesEnabled(newValue);
settings.save();
}
private void portDidChange(String newValue) {
try {
int port = Integer.parseInt(newValue);
if (port < Settings.MIN_PORT) {
if (!settings.isPortValid(port)) {
settings.setPort(Settings.DEFAULT_PORT);
} else if (port < Settings.MAX_PORT) {
settings.setPort(port);
} else {
portField.setText(String.valueOf(Settings.MAX_PORT));
settings.setPort(port);
settings.save();
}
} catch (NumberFormatException e) {
portField.setText(String.valueOf(Settings.DEFAULT_PORT));
}
}
private void useIpv6DidChange(Boolean newValue) {
settings.setUseIpv6(newValue);
settings.save();
}
private void filterNumericKeyEvents(KeyEvent t) {
if (t.getCharacter() == null || t.getCharacter().length() == 0) {
return;

View File

@@ -10,7 +10,7 @@ package org.cryptomator.ui.controllers;
import java.net.URL;
import java.util.Comparator;
import java.util.ResourceBundle;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import javax.inject.Inject;
@@ -51,23 +51,23 @@ import javafx.scene.layout.GridPane;
import javafx.scene.text.Text;
import javafx.util.StringConverter;
public class UnlockController extends AbstractFXMLViewController {
public class UnlockController extends LocalizedFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class);
private final Application app;
private final Localization localization;
private final ExecutorService exec;
private final Lazy<FrontendFactory> frontendFactory;
private final Settings settings;
private final WindowsDriveLetters driveLetters;
private final ChangeListener<Character> driveLetterChangeListener = this::winDriveLetterDidChange;
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
private Optional<UnlockListener> listener = Optional.empty();
@Inject
public UnlockController(Application app, Localization localization, ExecutorService exec, Lazy<FrontendFactory> frontendFactory, Settings settings, WindowsDriveLetters driveLetters) {
super(localization);
this.app = app;
this.localization = localization;
this.exec = exec;
this.frontendFactory = frontendFactory;
this.settings = settings;
@@ -127,11 +127,6 @@ public class UnlockController extends AbstractFXMLViewController {
return getClass().getResource("/fxml/unlock.fxml");
}
@Override
protected ResourceBundle getFxmlResourceBundle() {
return localization;
}
private void vaultChanged(Vault newVault) {
if (newVault == null) {
return;
@@ -276,21 +271,24 @@ public class UnlockController extends AbstractFXMLViewController {
progressIndicator.setVisible(true);
downloadsPageLink.setVisible(false);
CharSequence password = passwordField.getCharacters();
exec.submit(() -> this.unlock(password));
exec.submit(() -> this.unlock(vault.get(), password));
}
private void unlock(CharSequence password) {
private void unlock(Vault vault, CharSequence password) {
try {
vault.get().activateFrontend(frontendFactory.get(), settings, password);
vault.get().reveal();
vault.activateFrontend(frontendFactory.get(), settings, password);
vault.reveal();
Platform.runLater(() -> {
messageText.setText(null);
listener.ifPresent(lstnr -> lstnr.didUnlock(vault));
});
} catch (InvalidPassphraseException e) {
Platform.runLater(() -> {
messageText.setText(localization.getString("unlock.errorMessage.wrongPassword"));
passwordField.requestFocus();
});
} catch (UnsupportedVaultFormatException e) {
LOG.warn("Unable to unlock vault: " + e.getMessage());
Platform.runLater(() -> {
downloadsPageLink.setVisible(true);
if (e.isVaultOlderThanSoftware()) {
@@ -314,4 +312,15 @@ public class UnlockController extends AbstractFXMLViewController {
}
}
/* callback */
public void setListener(UnlockListener listener) {
this.listener = Optional.ofNullable(listener);
}
@FunctionalInterface
interface UnlockListener {
void didUnlock(Vault vault);
}
}

View File

@@ -10,7 +10,6 @@ package org.cryptomator.ui.controllers;
import java.net.URL;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import javax.inject.Inject;
@@ -46,12 +45,11 @@ import javafx.stage.PopupWindow.AnchorLocation;
import javafx.stage.Stage;
import javafx.util.Duration;
public class UnlockedController extends AbstractFXMLViewController {
public class UnlockedController extends LocalizedFXMLViewController {
private static final int IO_SAMPLING_STEPS = 100;
private static final double IO_SAMPLING_INTERVAL = 0.25;
private final Localization localization;
private final Stage macWarningsWindow = new Stage();
private final MacWarningsController macWarningsController;
private final ExecutorService exec;
@@ -61,7 +59,7 @@ public class UnlockedController extends AbstractFXMLViewController {
@Inject
public UnlockedController(Localization localization, Provider<MacWarningsController> macWarningsControllerProvider, ExecutorService exec) {
this.localization = localization;
super(localization);
this.macWarningsController = macWarningsControllerProvider.get();
this.exec = exec;
@@ -97,11 +95,6 @@ public class UnlockedController extends AbstractFXMLViewController {
return getClass().getResource("/fxml/unlocked.fxml");
}
@Override
protected ResourceBundle getFxmlResourceBundle() {
return localization;
}
private void vaultChanged(Vault newVault) {
if (newVault == null) {
return;
@@ -261,10 +254,6 @@ public class UnlockedController extends AbstractFXMLViewController {
/* callback */
public LockListener getListener() {
return listener.orElse(null);
}
public void setListener(LockListener listener) {
this.listener = Optional.ofNullable(listener);
}

View File

@@ -0,0 +1,128 @@
package org.cryptomator.ui.controllers;
import java.net.URL;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import javax.inject.Inject;
import org.cryptomator.ui.model.UpgradeInstruction;
import org.cryptomator.ui.model.UpgradeInstruction.UpgradeFailedException;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.settings.Localization;
import org.fxmisc.easybind.EasyBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Platform;
import javafx.beans.binding.Binding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
public class UpgradeController extends LocalizedFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(UpgradeController.class);
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
private final ExecutorService exec;
private final Binding<Optional<UpgradeInstruction>> upgradeInstruction = EasyBind.monadic(vault).map(Vault::availableUpgrade);
private Optional<UpgradeListener> listener = Optional.empty();
@Inject
public UpgradeController(Localization localization, ExecutorService exec) {
super(localization);
this.exec = exec;
}
@FXML
private Label upgradeLabel;
@FXML
private Button upgradeButton;
@FXML
private ProgressIndicator progressIndicator;
@FXML
private Label errorLabel;
@Override
protected void initialize() {
upgradeLabel.textProperty().bind(EasyBind.monadic(upgradeInstruction).map(instruction -> {
return instruction.map(this::upgradeNotification).orElse("");
}).orElse(""));
EasyBind.subscribe(vault, this::vaultChanged);
}
@Override
protected URL getFxmlResourceUrl() {
return getClass().getResource("/fxml/upgrade.fxml");
}
private void vaultChanged(Vault newVault) {
errorLabel.setText(null);
}
// ****************************************
// Upgrade label
// ****************************************
private String upgradeNotification(UpgradeInstruction instruction) {
return instruction.getNotification(vault.get(), localization);
}
// ****************************************
// Upgrade button
// ****************************************
@FXML
private void didClickUpgradeButton(ActionEvent event) {
upgradeInstruction.getValue().ifPresent(this::upgrade);
}
private void upgrade(UpgradeInstruction instruction) {
Vault v = vault.getValue();
Objects.requireNonNull(v);
progressIndicator.setVisible(true);
upgradeButton.setDisable(true);
exec.submit(() -> {
if (!instruction.isApplicable(v)) {
LOG.error("No upgrade needed for " + v.path().getValue());
throw new IllegalStateException("No ugprade needed for " + v.path().getValue());
}
try {
instruction.upgrade(v, localization);
Platform.runLater(() -> {
progressIndicator.setVisible(false);
upgradeButton.setDisable(false);
listener.ifPresent(UpgradeListener::didUpgrade);
});
} catch (UpgradeFailedException e) {
Platform.runLater(() -> {
errorLabel.setText(e.getLocalizedMessage());
progressIndicator.setVisible(false);
upgradeButton.setDisable(false);
});
}
});
}
/* callback */
public void setListener(UpgradeListener listener) {
this.listener = Optional.ofNullable(listener);
}
@FunctionalInterface
interface UpgradeListener {
void didUpgrade();
}
}

View File

@@ -15,7 +15,6 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import javax.inject.Inject;
@@ -48,20 +47,19 @@ import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
@Singleton
public class WelcomeController extends AbstractFXMLViewController {
public class WelcomeController extends LocalizedFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class);
private final Application app;
private final Localization localization;
private final Settings settings;
private final Comparator<String> semVerComparator;
private final ExecutorService executor;
@Inject
public WelcomeController(Application app, Localization localization, Settings settings, @Named("SemVer") Comparator<String> semVerComparator, ExecutorService executor) {
super(localization);
this.app = app;
this.localization = localization;
this.settings = settings;
this.semVerComparator = semVerComparator;
this.executor = executor;
@@ -93,11 +91,6 @@ public class WelcomeController extends AbstractFXMLViewController {
return getClass().getResource("/fxml/welcome.fxml");
}
@Override
protected ResourceBundle getFxmlResourceBundle() {
return localization;
}
// ****************************************
// Check for updates
// ****************************************

View File

@@ -9,9 +9,8 @@
package org.cryptomator.ui.controls;
import org.cryptomator.ui.model.Vault;
import org.fxmisc.easybind.EasyBind;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ContextMenu;
@@ -21,15 +20,18 @@ import javafx.scene.control.Tooltip;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
public class DirectoryListCell extends DraggableListCell<Vault>implements ChangeListener<Boolean> {
public class DirectoryListCell extends DraggableListCell<Vault> {
// fill: #FD4943, stroke: #E1443F
private static final Color RED_FILL = Color.rgb(253, 73, 67);
private static final Color RED_STROKE = Color.rgb(225, 68, 63);
// fill: #FFBF2F, stroke: #E4AC36
// private static final Color YELLOW_FILL = Color.rgb(255, 191, 47);
// private static final Color YELLOW_STROKE = Color.rgb(228, 172, 54);
// fill: #28CA40, stroke: #30B740
private static final Color GREEN_FILL = Color.rgb(40, 202, 64);
private static final Color GREEN_STROKE = Color.rgb(48, 183, 64);
@@ -46,58 +48,33 @@ public class DirectoryListCell extends DraggableListCell<Vault>implements Change
hbox.setPrefWidth(1);
vbox.setFillWidth(true);
nameText.textProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::name));
nameText.textFillProperty().bind(this.textFillProperty());
nameText.fontProperty().bind(this.fontProperty());
pathText.textProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::displayablePath));
pathText.setTextOverrun(OverrunStyle.ELLIPSIS);
pathText.getStyleClass().add("detail-label");
statusIndicator.fillProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).filter(Boolean.TRUE::equals).map(unlocked -> GREEN_FILL).orElse(RED_FILL));
statusIndicator.strokeProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).filter(Boolean.TRUE::equals).map(unlocked -> GREEN_STROKE).orElse(RED_STROKE));
tooltipProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::path).map(p -> new Tooltip(p.toString())));
contextMenuProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).map(unlocked -> {
return unlocked ? null : vaultContextMenu;
}));
setGraphic(hbox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
@Override
protected void updateItem(Vault item, boolean empty) {
final Vault oldItem = super.getItem();
if (oldItem != null) {
oldItem.unlockedProperty().removeListener(this);
}
super.updateItem(item, empty);
if (item == null) {
nameText.setText(null);
pathText.setText(null);
setTooltip(null);
setContextMenu(null);
statusIndicator.setVisible(false);
} else {
nameText.setText(item.getName());
pathText.setText(item.getDisplayablePath());
setTooltip(new Tooltip(item.getPath().toString()));
statusIndicator.setVisible(true);
item.unlockedProperty().addListener(this);
updateStatusIndicator();
updateContextMenu();
}
}
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
updateStatusIndicator();
updateContextMenu();
}
private void updateStatusIndicator() {
final Paint fillColor = getItem().isUnlocked() ? GREEN_FILL : RED_FILL;
final Paint strokeColor = getItem().isUnlocked() ? GREEN_STROKE : RED_STROKE;
statusIndicator.setFill(fillColor);
statusIndicator.setStroke(strokeColor);
}
private void updateContextMenu() {
if (getItem().isUnlocked()) {
this.setContextMenu(null);
} else {
this.setContextMenu(vaultContextMenu);
}
}

View File

@@ -0,0 +1,40 @@
package org.cryptomator.ui.model;
import org.cryptomator.ui.settings.Localization;
public interface UpgradeInstruction {
static UpgradeInstruction[] AVAILABLE_INSTRUCTIONS = {new UpgradeVersion3DropBundleExtension()};
/**
* @return Localized string to display to the user when an upgrade is needed.
*/
String getNotification(Vault vault, Localization localization);
/**
* Upgrades a vault. Might take a moment, should be run in a background thread.
*/
void upgrade(Vault vault, Localization localization) throws UpgradeFailedException;
/**
* Determines in O(1), if an upgrade can be applied to a vault.
*
* @return <code>true</code> if and only if the vault can be migrated to a newer version without the risk of data losses.
*/
boolean isApplicable(Vault vault);
/**
* Thrown when data migration failed.
*/
public class UpgradeFailedException extends Exception {
UpgradeFailedException() {
}
UpgradeFailedException(String message) {
super(message);
}
}
}

View File

@@ -0,0 +1,55 @@
package org.cryptomator.ui.model;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.ui.settings.Localization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Platform;
class UpgradeVersion3DropBundleExtension implements UpgradeInstruction {
private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion3DropBundleExtension.class);
@Override
public String getNotification(Vault vault, Localization localization) {
String fmt = localization.getString("upgrade.version3dropBundleExtension.msg");
Path path = vault.path().getValue();
String oldVaultName = path.getFileName().toString();
String newVaultName = StringUtils.removeEnd(oldVaultName, Vault.VAULT_FILE_EXTENSION);
return String.format(fmt, oldVaultName, newVaultName);
}
@Override
public void upgrade(Vault vault, Localization localization) throws UpgradeFailedException {
Path path = vault.path().getValue();
String oldVaultName = path.getFileName().toString();
String newVaultName = StringUtils.removeEnd(oldVaultName, Vault.VAULT_FILE_EXTENSION);
Path newPath = path.resolveSibling(newVaultName);
if (Files.exists(newPath)) {
String fmt = localization.getString("upgrade.version3dropBundleExtension.err.alreadyExists");
String msg = String.format(fmt, newPath);
throw new UpgradeFailedException(msg);
} else {
try {
Files.move(path, path.resolveSibling(newVaultName));
Platform.runLater(() -> {
vault.setPath(newPath);
});
} catch (IOException e) {
LOG.error("Vault migration failed", e);
throw new UpgradeFailedException();
}
}
}
@Override
public boolean isApplicable(Vault vault) {
return vault.path().getValue().getFileName().toString().endsWith(Vault.VAULT_FILE_EXTENSION);
}
}

View File

@@ -11,10 +11,12 @@ package org.cryptomator.ui.model;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
@@ -28,6 +30,7 @@ import org.cryptomator.common.LazyInitializer;
import org.cryptomator.common.Optionals;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.charsets.NormalizedNameFileSystem;
import org.cryptomator.filesystem.crypto.CryptoFileSystemDelegate;
import org.cryptomator.filesystem.crypto.CryptoFileSystemFactory;
import org.cryptomator.filesystem.nio.NioFileSystem;
@@ -42,12 +45,17 @@ import org.cryptomator.ui.settings.Settings;
import org.cryptomator.ui.util.DeferredClosable;
import org.cryptomator.ui.util.DeferredCloser;
import org.cryptomator.ui.util.FXThreads;
import org.fxmisc.easybind.EasyBind;
import com.google.common.collect.ImmutableMap;
import javafx.application.Platform;
import javafx.beans.binding.Binding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@@ -55,7 +63,7 @@ public class Vault implements CryptoFileSystemDelegate {
public static final String VAULT_FILE_EXTENSION = ".cryptomator";
private final Path path;
private final ObjectProperty<Path> path;
private final DeferredCloser closer;
private final ShorteningFileSystemFactory shorteningFileSystemFactory;
private final CryptoFileSystemFactory cryptoFileSystemFactory;
@@ -73,20 +81,20 @@ public class Vault implements CryptoFileSystemDelegate {
* Package private constructor, use {@link VaultFactory}.
*/
Vault(Path vaultDirectoryPath, ShorteningFileSystemFactory shorteningFileSystemFactory, CryptoFileSystemFactory cryptoFileSystemFactory, DeferredCloser closer) {
this.path = vaultDirectoryPath;
this.path = new SimpleObjectProperty<Path>(vaultDirectoryPath);
this.closer = closer;
this.shorteningFileSystemFactory = shorteningFileSystemFactory;
this.cryptoFileSystemFactory = cryptoFileSystemFactory;
try {
setMountName(getName());
setMountName(name().getValue());
} catch (IllegalArgumentException e) {
// mount name needs to be set by the user explicitly later
}
}
private FileSystem getNioFileSystem() {
return LazyInitializer.initializeLazily(nioFileSystem, () -> NioFileSystem.rootedAt(path));
return LazyInitializer.initializeLazily(nioFileSystem, () -> NioFileSystem.rootedAt(path.getValue()));
}
// ******************************************************************************
@@ -119,7 +127,8 @@ public class Vault implements CryptoFileSystemDelegate {
FileSystem fs = getNioFileSystem();
FileSystem shorteningFs = shorteningFileSystemFactory.get(fs);
FileSystem cryptoFs = cryptoFileSystemFactory.unlockExisting(shorteningFs, passphrase, this);
StatsFileSystem statsFs = new StatsFileSystem(cryptoFs);
FileSystem normalizingFs = new NormalizedNameFileSystem(cryptoFs, SystemUtils.IS_OS_MAC_OSX ? Form.NFD : Form.NFC);
StatsFileSystem statsFs = new StatsFileSystem(normalizingFs);
statsFileSystem = Optional.of(statsFs);
String contextPath = StringUtils.prependIfMissing(mountName, "/");
Frontend frontend = frontendFactory.create(statsFs, contextPath);
@@ -158,6 +167,16 @@ public class Vault implements CryptoFileSystemDelegate {
Optionals.ifPresent(filesystemFrontend.get(), Frontend::unmount);
}
public boolean needsUpgrade() {
return availableUpgrade().isPresent();
}
public Optional<UpgradeInstruction> availableUpgrade() {
return Arrays.stream(UpgradeInstruction.AVAILABLE_INSTRUCTIONS).filter(instruction -> {
return instruction.isApplicable(this);
}).findAny();
}
// ******************************************************************************
// Delegate methods
// ********************************************************************************/
@@ -180,31 +199,42 @@ public class Vault implements CryptoFileSystemDelegate {
return filesystemFrontend.get().map(Frontend::getWebDavUrl).orElseThrow(IllegalStateException::new);
}
public Path getPath() {
void setPath(Path path) {
this.path.set(path);
this.nioFileSystem.set(null);
}
public ReadOnlyObjectProperty<Path> path() {
return path;
}
public String getDisplayablePath() {
public Binding<String> displayablePath() {
Path homeDir = Paths.get(SystemUtils.USER_HOME);
if (path.startsWith(homeDir)) {
Path relativePath = homeDir.relativize(path);
String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/";
return homePrefix + relativePath.toString();
} else {
return path.toString();
}
return EasyBind.map(path, p -> {
if (p.startsWith(homeDir)) {
Path relativePath = homeDir.relativize(p);
String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/";
return homePrefix + relativePath.toString();
} else {
return path.getValue().toString();
}
});
}
/**
* @return Directory name without preceeding path components and file extension
*/
public String getName() {
return StringUtils.removeEnd(path.getFileName().toString(), VAULT_FILE_EXTENSION);
public Binding<String> name() {
return EasyBind.map(path, p -> p.getFileName().toString());
}
public boolean doesVaultDirectoryExist() {
return Files.isDirectory(path.getValue());
}
public boolean isValidVaultDirectory() {
try {
return cryptoFileSystemFactory.isValidVaultStructure(getNioFileSystem());
return doesVaultDirectoryExist() && cryptoFileSystemFactory.isValidVaultStructure(getNioFileSystem());
} catch (UncheckedIOException e) {
return false;
}
@@ -292,17 +322,17 @@ public class Vault implements CryptoFileSystemDelegate {
@Override
public int hashCode() {
return path.hashCode();
return path.getValue().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Vault) {
final Vault other = (Vault) obj;
return this.path.equals(other.path);
return this.path.getValue().equals(other.path.getValue());
} else {
return false;
}
}
}
}

View File

@@ -55,7 +55,7 @@ public class VaultObjectMapperProvider implements Provider<ObjectMapper> {
@Override
public void serialize(Vault value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("path", value.getPath().toString());
jgen.writeStringField("path", value.path().getValue().toString());
jgen.writeStringField("mountName", value.getMountName());
final Character winDriveLetter = value.getWinDriveLetter();
if (winDriveLetter != null) {

View File

@@ -1,27 +1,70 @@
package org.cryptomator.ui.settings;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Objects;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class Localization extends ResourceBundle {
private static final Logger LOG = LoggerFactory.getLogger(Localization.class);
private static final String LOCALIZATION_DEFAULT_FILE = "/localization/en.txt";
private static final String LOCALIZATION_FILENAME_FMT = "/localization/%s.txt";
private static final String LOCALIZATION_FILE = String.format(LOCALIZATION_FILENAME_FMT, Locale.getDefault().getLanguage());
private final ResourceBundle fallback;
private final ResourceBundle localized;
@Inject
public Localization() {
this.parent = ResourceBundle.getBundle("localization");
try (InputStream in = getClass().getResourceAsStream(LOCALIZATION_DEFAULT_FILE)) {
Objects.requireNonNull(in);
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
this.fallback = new PropertyResourceBundle(reader);
LOG.info("Loaded localization from {}", LOCALIZATION_FILE);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
try (InputStream in = getClass().getResourceAsStream(LOCALIZATION_FILE)) {
if (in != null) {
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
this.localized = new PropertyResourceBundle(reader);
} else {
this.localized = this.fallback;
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
protected Object handleGetObject(String key) {
return parent.getObject(key);
return localized.containsKey(key) ? localized.getObject(key) : fallback.getObject(key);
}
@Override
public Enumeration<String> getKeys() {
return parent.getKeys();
Collection<String> keys = CollectionUtils.union(localized.keySet(), fallback.keySet());
return Collections.enumeration(keys);
}
}

View File

@@ -11,6 +11,7 @@ package org.cryptomator.ui.settings;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.cryptomator.ui.model.Vault;
@@ -23,10 +24,12 @@ public class Settings implements Serializable {
private static final long serialVersionUID = 7609959894417878744L;
public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65535;
public static final int DEFAULT_PORT = 0;
public static final int DEFAULT_PORT = 42427;
public static final boolean DEFAULT_USE_IPV6 = false;
public static final Integer DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
private final Consumer<Settings> saveCmd;
@JsonProperty("directories")
private List<Vault> directories;
@@ -35,7 +38,7 @@ public class Settings implements Serializable {
@JsonProperty("port")
private Integer port;
@JsonProperty("useIpv6")
private Boolean useIpv6;
@@ -45,8 +48,12 @@ public class Settings implements Serializable {
/**
* Package-private constructor; use {@link SettingsProvider}.
*/
Settings() {
Settings(Consumer<Settings> saveCmd) {
this.saveCmd = saveCmd;
}
public void save() {
saveCmd.accept(this);
}
/* Getter/Setter */
@@ -86,8 +93,8 @@ public class Settings implements Serializable {
}
}
private boolean isPortValid(int port) {
return port == DEFAULT_PORT || port >= MIN_PORT && port <= MAX_PORT;
public boolean isPortValid(int port) {
return port == DEFAULT_PORT || port >= MIN_PORT && port <= MAX_PORT || port == 0;
}
public boolean shouldUseIpv6() {

View File

@@ -16,6 +16,12 @@ import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;
import javax.inject.Named;
@@ -23,7 +29,6 @@ import javax.inject.Provider;
import javax.inject.Singleton;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.util.DeferredCloser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,9 +37,10 @@ import com.fasterxml.jackson.databind.ObjectMapper;
@Singleton
public class SettingsProvider implements Provider<Settings> {
private static final Logger LOG = LoggerFactory.getLogger(Settings.class);
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
private static final Path SETTINGS_DIR;
private static final String SETTINGS_FILE = "settings.json";
private static final long SAVE_DELAY_MS = 1000;
static {
final String appdata = System.getenv("APPDATA");
@@ -52,12 +58,12 @@ public class SettingsProvider implements Provider<Settings> {
}
}
private final DeferredCloser deferredCloser;
private final ObjectMapper objectMapper;
private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor();
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
@Inject
public SettingsProvider(DeferredCloser deferredCloser, @Named("VaultJsonMapper") ObjectMapper objectMapper) {
this.deferredCloser = deferredCloser;
public SettingsProvider(@Named("VaultJsonMapper") ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@@ -72,29 +78,39 @@ public class SettingsProvider implements Provider<Settings> {
@Override
public Settings get() {
Settings settings = null;
final Settings settings = new Settings(this::scheduleSave);
try {
final Path settingsPath = getSettingsPath();
final InputStream in = Files.newInputStream(settingsPath, StandardOpenOption.READ);
settings = objectMapper.readValue(in, Settings.class);
settings.getDirectories().removeIf(v -> !v.isValidVaultDirectory());
objectMapper.readerForUpdating(settings).readValue(in);
LOG.info("Settings loaded from " + settingsPath);
} catch (IOException e) {
LOG.warn("Failed to load settings, creating new one.");
settings = new Settings();
LOG.info("Failed to load settings, creating new one.");
}
deferredCloser.closeLater(settings, this::save);
return settings;
}
private void save(Settings settings) {
private void scheduleSave(Settings settings) {
if (settings == null) {
return;
}
ScheduledFuture<?> saveCmd = saveScheduler.schedule(() -> {
this.save(settings);
} , SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
ScheduledFuture<?> previousSaveCmd = scheduledSaveCmd.getAndSet(saveCmd);
if (previousSaveCmd != null) {
previousSaveCmd.cancel(false);
}
}
private void save(Settings settings) {
Objects.requireNonNull(settings);
try {
final Path settingsPath = getSettingsPath();
Files.createDirectories(settingsPath.getParent());
final OutputStream out = Files.newOutputStream(settingsPath, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
objectMapper.writeValue(out, settings);
LOG.info("Settings saved to " + settingsPath);
} catch (IOException e) {
LOG.error("Failed to save settings.", e);
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2014 Sebastian Stenzel
This file is licensed under the terms of the MIT license.
See the LICENSE.txt file for more info.
Contributors:
Sebastian Stenzel - initial API and implementation
-->
<?import java.lang.String?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox prefWidth="400.0" prefHeight="400.0" spacing="24.0" alignment="CENTER" xmlns:fx="http://javafx.com/fxml" cacheShape="true" cache="true">
<Label text="%notfound.label" textAlignment="CENTER" wrapText="true" cacheShape="true" cache="true"/>
</VBox>

View File

@@ -38,7 +38,7 @@
<TextField GridPane.rowIndex="1" GridPane.columnIndex="1" fx:id="portField" cacheShape="true" cache="true" promptText="%settings.port.prompt" />
<!-- Row 2 -->
<Label GridPane.rowIndex="2" GridPane.columnIndex="0" text="%settings.useipv6.label" cacheShape="true" cache="true" />
<Label GridPane.rowIndex="2" GridPane.columnIndex="0" fx:id="useIpv6Label" text="%settings.useipv6.label" cacheShape="true" cache="true" />
<CheckBox GridPane.rowIndex="2" GridPane.columnIndex="1" fx:id="useIpv6Checkbox" cacheShape="true" cache="true" />
</children>
</GridPane>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2014 Sebastian Stenzel
This file is licensed under the terms of the MIT license.
See the LICENSE.txt file for more info.
Contributors:
Sebastian Stenzel - initial API and implementation
-->
<?import java.lang.String?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<VBox prefWidth="400.0" prefHeight="400.0" spacing="24.0" alignment="CENTER" xmlns:fx="http://javafx.com/fxml" cacheShape="true" cache="true">
<Label fx:id="upgradeLabel" textAlignment="CENTER" wrapText="true"/>
<Button fx:id="upgradeButton" text="%upgrade.button" prefWidth="150.0" onAction="#didClickUpgradeButton" cacheShape="true" cache="true" />
<ProgressIndicator progress="-1" fx:id="progressIndicator" visible="false" cacheShape="true" cache="true" cacheHint="SPEED" />
<Label fx:id="errorLabel" textAlignment="CENTER" wrapText="true"/>
</VBox>

View File

@@ -0,0 +1,88 @@
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# Markus Kreusch
# Michael Schmetter
# Sebastian Wiesendahl
# Tim Marius Wunderlich
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Klicken Sie hier, um neue Tresore hinzuzufügen
main.directoryList.contextMenu.remove = Aus Liste entfernen
main.directoryList.contextMenu.changePassword = Passwort ändern
main.addDirectory.contextMenu.new = Tresor erstellen
main.addDirectory.contextMenu.open = Tresor öffnen
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Auf Updates prüfen...
welcome.newVersionMessage = Version %s kann heruntergeladen werden. Aktuelle Version %s.
# initialize.fxml
initialize.label.password = Passwort
initialize.label.retypePassword = Passwort bestätigen
initialize.button.ok = Tresor erstellen
initialize.messageLabel.alreadyInitialized = Tresor bereits vorhanden
initialize.messageLabel.initializationFailed = Fehler beim Initialisieren. Details in der Log-Datei.
# notfound.fxml
notfound.label = Tresor konnte nicht gefunden werden.\nWurde er verschoben?
# upgrade.fxml
upgrade.button = Tresor aktualisieren
upgrade.version3dropBundleExtension.msg = Dieser Tresor muss auf ein neueres Format aktualisiert werden.\n"%1$s" wird in "%2$s" umbenannt.\nStellen Sie bitte sicher, dass derzeit keine Synchronisation stattfindet.
upgrade.version3dropBundleExtension.err.alreadyExists = Migration fehlgeschlagen.\n"%s" existiert bereits.
# unlock.fxml
unlock.label.password = Passwort
unlock.label.mountName = Laufwerksname
unlock.label.winDriveLetter = Laufwerksbuchstabe
unlock.label.downloadsPageLink = Alle Cryptomator Versionen
unlock.label.advancedHeading = Erweiterte Optionen
unlock.button.unlock = Tresor entsperren
unlock.button.advancedOptions.show = Weitere Optionen
unlock.button.advancedOptions.hide = Weniger Optionen
unlock.choicebox.winDriveLetter.auto = Automatisch ermitteln
unlock.errorMessage.wrongPassword = Falsches Passwort
unlock.errorMessage.mountingFailed = Verbindung fehlgeschlagen. Details in der Log-Datei.
unlock.errorMessage.unsupportedKeyLengthInstallJCE = Entschlüsselung fehlgeschlagen. Bitte die Oracle JCE Unlimited Strength Policy installieren.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Tresor nicht unterstützt. Der Tresor wurde mit einer älteren Version von Cryptomator erstellt.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Tresor nicht unterstützt. Der Tresor wurde mit einer neueren Version von Cryptomator erstellt.
unlock.messageLabel.startServerFailed = Starten des WebDAV-Servers fehlgeschlagen.
# change_password.fxml
changePassword.label.oldPassword = Altes Passwort
changePassword.label.newPassword = Neues Passwort
changePassword.label.retypePassword = Passwort bestätigen
changePassword.label.downloadsPageLink = Alle Cryptomator Versionen
changePassword.button.change = Passwort ändern
changePassword.errorMessage.wrongPassword = Falsches Passwort
changePassword.errorMessage.decryptionFailed = Entschlüsselung fehlgeschlagen
changePassword.errorMessage.unsupportedKeyLengthInstallJCE = Entschlüsselung fehlgeschlagen. Bitte die Oracle JCE Unlimited Strength Policy installieren.
changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Tresor nicht unterstützt. Der Tresor wurde mit einer älteren Version von Cryptomator erstellt.
changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault = Tresor nicht unterstützt. Der Tresor wurde mit einer neueren Version von Cryptomator erstellt.
changePassword.infoMessage.success = Passwort geändert
# unlocked.fxml
unlocked.button.lock = Tresor sperren
unlocked.moreOptions.reveal = Laufwerk anzeigen
unlocked.moreOptions.copyUrl = WebDAV-URL kopieren
unlocked.label.revealFailed = Befehl fehlgeschlagen
unlocked.label.unmountFailed = Trennen des Laufwerks fehlgeschlagen
unlocked.label.statsEncrypted = verschlüsselt
unlocked.label.statsDecrypted = entschlüsselt
unlocked.ioGraph.yAxis.label = Durchsatz (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = Achtung - Kompromittierte Datei in %s
# "potentially detected" or "potentially malicious"?
macWarnings.message = Cryptomator hat möglicherweise unerlaubte Veränderungen in den folgenden Dateien erkannt\:
macWarnings.moreInformationButton = Mehr erfahren
macWarnings.whitelistButton = Trotzdem entschlüsseln
# settings.fxml
settings.version.label = Version %s
settings.checkForUpdates.label = Auf Updates prüfen
settings.port.label = WebDAV Port *
settings.port.prompt = 0 \= Automatisch wählen
settings.useipv6.label = IPv6-Literal nutzen
settings.requiresRestartLabel = * benötigt Neustart von Cryptomator
# tray icon
tray.menu.open = Öffnen
tray.menu.quit = Beenden
tray.infoMsg.title = Cryptomator läuft noch
tray.infoMsg.msg = Cryptomator läuft noch. Mit dem Tray-Icon beenden.
tray.infoMsg.msg.osx = Cryptomator läuft noch. Über die Menüleiste beenden.

View File

@@ -1,11 +1,9 @@
#-------------------------------------------------------------------------------
# Copyright (c) 2014 Sebastian Stenzel
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# Sebastian Stenzel - initial API and implementation
#-------------------------------------------------------------------------------
# Sebastian Stenzel - initial translation
app.name=Cryptomator
@@ -27,6 +25,15 @@ initialize.button.ok=Create vault
initialize.messageLabel.alreadyInitialized=Vault already initialized
initialize.messageLabel.initializationFailed=Could not initialize vault. See logfile for details.
# notfound.fxml
notfound.label=Vault couldn't be found. Has it been moved?
# upgrade.fxml
upgrade.button=Upgrade vault
upgrade.version3dropBundleExtension.msg=This vault needs to be migrated to a newer format.\n"%1$s" will be renamed to "%2$s".\nPlease make sure synchronization has finished before proceeding.
upgrade.version3dropBundleExtension.err.alreadyExists=Automatic migration failed.\n"%s" already exists.
# unlock.fxml
unlock.label.password=Password
unlock.label.mountName=Drive name

View File

@@ -0,0 +1,87 @@
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributos:
# Jay
# Sebastian Wiesendahl
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Click aquí para añadir una caja fuerte
# should it be imperative?
main.directoryList.contextMenu.remove = Eliminar de la lista
main.directoryList.contextMenu.changePassword = Cambiar la contraseña
main.addDirectory.contextMenu.new = Crear una nueva caja fuerte
main.addDirectory.contextMenu.open = Abrir una caja fuerte existente
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Chequando por actualizaciónes...
welcome.newVersionMessage = Se puede bajar version %s. Este es %s.
# initialize.fxml
initialize.label.password = Contraseña
initialize.label.retypePassword = Reintroduzca contraseña
initialize.button.ok = Crear caja fuerte
initialize.messageLabel.alreadyInitialized = Caja fuerte ya está inicializado
initialize.messageLabel.initializationFailed = No se pudo inicializar la caja fuerte. Ver archivo de registro para detalles.
# notfound.fxml
notfound.label = No se pudo encontrar la caja fuerte. Se movió a otro lugar?
# upgrade.fxml
upgrade.button = Actualizar caja fuerte
upgrade.version3dropBundleExtension.msg = Este caja fuerte se debe actualizar a un nuevo formato.\n"%1$s" se renombra a "%2$s".\nPor favor aseguranse que la sincronización ya se terminó antes de continuar.
upgrade.version3dropBundleExtension.err.alreadyExists = Migración automática ha fallado.\n"%s" ya existe.
# unlock.fxml
unlock.label.password = Contraseña
unlock.label.mountName = Nombre del disco
unlock.label.winDriveLetter = Letra del disco
unlock.label.downloadsPageLink = Todas las versiones de Cryptomator
unlock.label.advancedHeading = Opciones avanzadas
unlock.button.unlock = Abrir caja fuerte
unlock.button.advancedOptions.show = Más opciones
unlock.button.advancedOptions.hide = Menos opciones
unlock.choicebox.winDriveLetter.auto = Asignar automáticamente
unlock.errorMessage.wrongPassword = Contraseña incorrecta
unlock.errorMessage.mountingFailed = Monteo ha fallado. Ver archivo del registro para detalles.
unlock.errorMessage.unsupportedKeyLengthInstallJCE = Decifración ha fallado. Por favor instala archivos de la Oracle JCE Unlimited Strength Policy.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Caja fuerte insupportado. Este caja se ha creado con una versión pasada de Cryptomator.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Caja fuerte insupportado. Este caja se ha creado con una versión nueva de Cryptomator.
unlock.messageLabel.startServerFailed = Iniciación del servidor de WebDAV ha fallado.
# change_password.fxml
changePassword.label.oldPassword = Contraseña anterior
# Can also use "current password" = "contraseña actual"
changePassword.label.newPassword = Nueva contraseña
changePassword.label.retypePassword = Reintroduzca contraseña
changePassword.label.downloadsPageLink = Todas las versiones de Cryptomator
changePassword.button.change = Cambiar contraseña
changePassword.errorMessage.wrongPassword = Contraseña incorrecta
changePassword.errorMessage.decryptionFailed = Decifración ha fallado
changePassword.errorMessage.unsupportedKeyLengthInstallJCE = Decifración ha fallado. Por favor instala Oracle JCE Unlimited Strength Policy.
changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Caja fuerte insupportado. Este caja se ha creado con una versión pasada de Cryptomator.
changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault = Caja fuerte insupportado. Este caja se ha creado con una versión nueva de Cryptomator.
changePassword.infoMessage.success = Contraseña se ha cambiado
# unlocked.fxml
unlocked.button.lock = Encerrar caja fuerte
unlocked.moreOptions.reveal = Revelar disco
unlocked.moreOptions.copyUrl = Copiar URL de WebDAV
unlocked.label.revealFailed = Comando ha fallado
unlocked.label.unmountFailed = Expulsar el disco ha fallado
unlocked.label.statsEncrypted = cifrado
unlocked.label.statsDecrypted = decifrado
unlocked.ioGraph.yAxis.label = Procesamiento (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = Peligro - archivo corrupto en %s
macWarnings.message = Cryptomator ha detectado corrupciones malciosas en los seguientes archivos\:
macWarnings.moreInformationButton = Aprende más
macWarnings.whitelistButton = Decifrar selecionado de todos modos
# settings.fxml
settings.version.label = Version %s
settings.checkForUpdates.label = Chequear por actualizaciones
settings.port.label = Puerta de WebDAV *
settings.port.prompt = 0 \= Elige automáticamente
settings.useipv6.label = Usa literal de IPv6
settings.requiresRestartLabel = * Cryptomator se necesita reiniciar
# tray icon
tray.menu.open = Abrir
tray.menu.quit = Salir
tray.infoMsg.title = Todavía en ejecución
tray.infoMsg.msg = Cryptomator todavía esta en ejecución. Sale del icono del tray.
tray.infoMsg.msg.osx = Cryptomator todavía esta en ejecución. Sale del icono de la barra del menú.

View File

@@ -0,0 +1,86 @@
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# Jean-Noël Charon
# Olivier Thomasson
# vesparny
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Cliquez ici pour ajouter un coffre
main.directoryList.contextMenu.remove = Retirer de la liste
main.directoryList.contextMenu.changePassword = Changer le mot de passe
main.addDirectory.contextMenu.new = Créer un nouveau coffre
main.addDirectory.contextMenu.open = Ouvrir un coffre existant
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Recherche de mise à jour...
welcome.newVersionMessage = La version %s peut-être téléchargée. Il s'agit de %s.
# initialize.fxml
initialize.label.password = Mot de passe
initialize.label.retypePassword = Confirmation
initialize.button.ok = Créer le coffre
initialize.messageLabel.alreadyInitialized = Coffre déjà initialisé
initialize.messageLabel.initializationFailed = Impossible d'initialiser le coffre. Voir le fichier de log pour plus de détails.
# notfound.fxml
notfound.label = Coffre introuvable. A t'il été déplacé?
# upgrade.fxml
upgrade.button = Mettre à niveau
upgrade.version3dropBundleExtension.msg = Ce coffre doit être converti dans un format plus récent.\n"%1$s" sera renommé en "%2$s".\nAssurez-vous que la synchronisation est terminée avant de continuer.
upgrade.version3dropBundleExtension.err.alreadyExists = La conversion automatique a échoué.\n"%s" existe déjà.
# unlock.fxml
unlock.label.password = Mot de passe
unlock.label.mountName = Nom du lecteur
unlock.label.winDriveLetter = Lettre du lecteur
unlock.label.downloadsPageLink = Toutes les versions de Cryptomator
unlock.label.advancedHeading = Options avancées
unlock.button.unlock = Déverrouiller le coffre
unlock.button.advancedOptions.show = Plus d'options
unlock.button.advancedOptions.hide = Moins d'options
unlock.choicebox.winDriveLetter.auto = Assigner automatiquement
unlock.errorMessage.wrongPassword = Mot de passe incorrect
unlock.errorMessage.mountingFailed = Echec du montage. Voir le fichier de log pour plus de détails.
unlock.errorMessage.unsupportedKeyLengthInstallJCE = Echec du décryptage. Veuillez installer la Policy Oracle "JCE Unlimited Strength Policy".
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Coffre non supporté. Ce coffre a été créé avec une ancienne version de Cryptomator.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Coffre non supporté. Ce coffre a été créé avec une version de Cryptomator plus récente.
unlock.messageLabel.startServerFailed = Le serveur WebDAV n'a pas pu démarrer.
# change_password.fxml
changePassword.label.oldPassword = Ancien mot de passe
changePassword.label.newPassword = Nouveau mot de passe
changePassword.label.retypePassword = Vérification
changePassword.label.downloadsPageLink = Toutes les versions de Cryptomator
changePassword.button.change = Modification du mot de masse
changePassword.errorMessage.wrongPassword = Mot de passe incorrect
changePassword.errorMessage.decryptionFailed = Echec du décryptage
changePassword.errorMessage.unsupportedKeyLengthInstallJCE = Echec du décryptage. Veuillez installer la Policy Oracle "JCE Unlimited Strength Policy".
changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Coffre non supporté. Ce coffre a été créé avec une ancienne version de Cryptomator.
changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault = Coffre non supporté. Ce coffre a été créé avec une version de Cryptomator plus récente.
changePassword.infoMessage.success = Password changed
# unlocked.fxml
unlocked.button.lock = Verrouiller le coffre
unlocked.moreOptions.reveal = Voir le lecteur
unlocked.moreOptions.copyUrl = Copier l'URL WebDAV
unlocked.label.revealFailed = Echec de la commande
unlocked.label.unmountFailed = Echec de l'éjection du lecteur
unlocked.label.statsEncrypted = crypté
unlocked.label.statsDecrypted = décryptage
unlocked.ioGraph.yAxis.label = Débit (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = Attention - Fichier corrompu dans %s
macWarnings.message = Cryptomator a détecté des corruptions de données dans les fichiers suivants\:
macWarnings.moreInformationButton = En savoir plus
macWarnings.whitelistButton = Décrypter tout de même
# settings.fxml
settings.version.label = Version %s
settings.checkForUpdates.label = Vérif. des mises à jour
settings.port.label = Port WebDAV *
settings.port.prompt = 0 \= Choix automatique
settings.useipv6.label = Utiliser un litéral IPv6
settings.requiresRestartLabel = * Redémarrage requis
# tray icon
tray.menu.open = Ouvrir
tray.menu.quit = Quitter
tray.infoMsg.title = Toujours en fonctionnement
tray.infoMsg.msg = Cryptomator est toujours en fonctionnement. Utiliser l'icône de la barre des tâches pour quitter.
tray.infoMsg.msg.osx = Cryptomator est toujours en fonctionnement. Utilisez la barre de menu pour quitter.

View File

@@ -0,0 +1,84 @@
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# Roland Burda
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Kattints ide egy széf létrehozásához
main.directoryList.contextMenu.remove = Eltávolítás listából
main.directoryList.contextMenu.changePassword = Jelszó megváltoztatása
main.addDirectory.contextMenu.new = Új széf létrehozása
main.addDirectory.contextMenu.open = Létez\u0151 széf megnyitása
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Frissítések keresése...
welcome.newVersionMessage = Új verzió érhet\u0151 el\: %s. Jelenlegi verzió\: %s.
# initialize.fxml
initialize.label.password = Jelszó
initialize.label.retypePassword = Jelszó ismét
initialize.button.ok = Széf létrehozása
initialize.messageLabel.alreadyInitialized = A széf már meg van nyitva
initialize.messageLabel.initializationFailed = Nem sikerült megnyitni a széfet. További információ a naplófájlban.
# notfound.fxml
notfound.label = Széf nem található. Lehetséges, hogy áthelyezésre került?
# upgrade.fxml
upgrade.button = Széf frissítése
upgrade.version3dropBundleExtension.msg = A széf új verzióra történ\u0151 migrációja szükséges. "%1$s" a következ\u0151re lesz átnevezve\: "%2$s". Kérlek gy\u0151z\u0151dj meg a szinkronizáció befejeztér\u0151l, miel\u0151tt más m\u0171veletet végeznél.
upgrade.version3dropBundleExtension.err.alreadyExists = Automatikus migráció meghíusúlt. "%s" már létezik.
# unlock.fxml
unlock.label.password = Jelszó
unlock.label.mountName = Meghajtó neve
unlock.label.winDriveLetter = Meghajtó bet\u0171jele
unlock.label.downloadsPageLink = Összes Cryptomator verzió
unlock.label.advancedHeading = Haladó beállítások
unlock.button.unlock = Széf feloldása
unlock.button.advancedOptions.show = További beállítások
unlock.button.advancedOptions.hide = Alapbeállítások
unlock.choicebox.winDriveLetter.auto = Automatikus hozzárendelés
unlock.errorMessage.wrongPassword = Hibás jelszó
unlock.errorMessage.mountingFailed = Meghajtó felcsatolása sikertelen. További információk a naplófájlban.
unlock.errorMessage.unsupportedKeyLengthInstallJCE = A titkosítás feloldása sikertelen. Kérlek telepítsd a "Oracle JCE Unlimited Strength Policy Files"-t.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Nem támogatott széf. Ez a széf a Cryptomator egy korábbi verziójával került létrehozásra.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Nem támogatott széf. Ez a széf a Cryptomator egy újabb verziójával került létrehozásra.
unlock.messageLabel.startServerFailed = WebDAV szerver indítása sikertelen.
# change_password.fxml
changePassword.label.oldPassword = Régi jelszó
changePassword.label.newPassword = Új jelszó
changePassword.label.retypePassword = Új jelszó ismét
changePassword.label.downloadsPageLink = Összes Cryptomator verzió
changePassword.button.change = Jelszó megváltoztatása
changePassword.errorMessage.wrongPassword = Hibás jelszó
changePassword.errorMessage.decryptionFailed = A titkosítás feloldása meghíusúlt
changePassword.errorMessage.unsupportedKeyLengthInstallJCE = A titkosítás feloldása sikertelen. Kérlek telepítsd a "Oracle JCE Unlimited Strength Policy"-t.
changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Nem támogatott széf. Ez a széf a Cryptomator egy korábbi verziójával került létrehozásra.
changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault = Nem támogatott széf. Ez a széf a Cryptomator egy újabb verziójával került létrehozásra.
changePassword.infoMessage.success = Jelszó megváltoztatva
# unlocked.fxml
unlocked.button.lock = Széf lezárása
unlocked.moreOptions.reveal = Meghajtó felfedése
unlocked.moreOptions.copyUrl = WebDAV URL másolása
unlocked.label.revealFailed = Parancs meghíusúlt
unlocked.label.unmountFailed = Meghajtó leválasztása sikertelen
unlocked.label.statsEncrypted = titkosított
unlocked.label.statsDecrypted = titkosítás feloldva
unlocked.ioGraph.yAxis.label = Teljesítmény (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = Veszély - Korrupt fájl a következ\u0151ben\: %s
macWarnings.message = Cryptomator potenciálisan rosszindulatú hibákat fedezett fel a következ\u0151 fájlokban\:
macWarnings.moreInformationButton = Tudj meg többet
macWarnings.whitelistButton = Kiválasztottak titkosításának feloldása mindenképp
# settings.fxml
settings.version.label = Verzió\: %s
settings.checkForUpdates.label = Frissítések keresése
settings.port.label = WebDAV Port *
settings.port.prompt = 0 \= Automatikus választás
settings.useipv6.label = Literális IPv6 használata
settings.requiresRestartLabel = * Cryptomator újraindítása szükséges
# tray icon
tray.menu.open = Megnyit
tray.menu.quit = Kilépés
tray.infoMsg.title = M\u0171velet folyamatban
tray.infoMsg.msg = Cryptomator még fut. A tálcán található ikon segítségével bezárhatod.
tray.infoMsg.msg.osx = Cryptomator még fut. A menüsávban található ikon segítségével bezárhatod.

View File

@@ -0,0 +1,84 @@
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# vesparny
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Clicca qui per aggiungere un vault
main.directoryList.contextMenu.remove = Rimuovi dalla lista
main.directoryList.contextMenu.changePassword = Cambia la password
main.addDirectory.contextMenu.new = Crea un nuovo vault
main.addDirectory.contextMenu.open = Apri un vault
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Verifica aggiornamenti...
welcome.newVersionMessage = La versione %s può essere scaricata. Questa è %s
# initialize.fxml
initialize.label.password = Password
initialize.label.retypePassword = Conferma password
initialize.button.ok = Crea un vault
initialize.messageLabel.alreadyInitialized = Vault già inizializzato
initialize.messageLabel.initializationFailed = Non è possibile inizializzare il vault. Controlla il file di log per dettagli.
# notfound.fxml
notfound.label = Il vault non può essere trovato. E' stato rimosso?
# upgrade.fxml
upgrade.button = Aggiorna vault
upgrade.version3dropBundleExtension.msg = Questo vault deve essere migrato ad un nuovo formato.\n"%1$s" verrà rinominato in "%2$s".\nPer favore verifica che la sincronizzazione sia finita prima di procedere\n
upgrade.version3dropBundleExtension.err.alreadyExists = Migrazione automatica fallita.\n"%s" esiste già.
# unlock.fxml
unlock.label.password = Password
unlock.label.mountName = nome del drive
unlock.label.winDriveLetter = lettera del drive
unlock.label.downloadsPageLink = Tutte le versioni di Cryptomator
unlock.label.advancedHeading = Opzioni avanzate
unlock.button.unlock = Sblocca vault
unlock.button.advancedOptions.show = Più opzioni
unlock.button.advancedOptions.hide = Meno opzioni
unlock.choicebox.winDriveLetter.auto = Assegna automaticamente
unlock.errorMessage.wrongPassword = Password errata
unlock.errorMessage.mountingFailed = Montaggio fallito. Controlla il file di log per dettagli.
unlock.errorMessage.unsupportedKeyLengthInstallJCE = Decriptaggio fallito.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Vault non supportato. Questo vault è stato creato con una versione di Cryptomator più vecchia.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Vault non supportato. Questo vault è stato creato con una versione di Cryptomator più recente.
unlock.messageLabel.startServerFailed = Avvio del server WebDAV fallito
# change_password.fxml
changePassword.label.oldPassword = Vecchia password
changePassword.label.newPassword = Nuova password
changePassword.label.retypePassword = Conferma password
changePassword.label.downloadsPageLink = Tutte le versioni di Cryptomator
changePassword.button.change = Cambia la password
changePassword.errorMessage.wrongPassword = Password errata
changePassword.errorMessage.decryptionFailed = Decriptaggio fallito
changePassword.errorMessage.unsupportedKeyLengthInstallJCE = Decriptaggio fallito. Per favore installa Oracle JCE Unlimited Strength Policy
changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Vault non supportato. Questo vault è stato creato con una versione di Cryptomator più recente.
changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault = Vault non supportato. Questo vault è stato creato con una versione di Cryptomator più vecchia.
changePassword.infoMessage.success = Password cambiata
# unlocked.fxml
unlocked.button.lock = Blocca vault
unlocked.moreOptions.reveal = Apri il disco
unlocked.moreOptions.copyUrl = Copia url WebDAV
unlocked.label.revealFailed = Comando fallito
unlocked.label.unmountFailed = Espulsione disco fallita
unlocked.label.statsEncrypted = criptato
unlocked.label.statsDecrypted = decriptato
unlocked.ioGraph.yAxis.label = Volume dati (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = Pericolo - File corroto in %s
macWarnings.message = Cryptomator ha individuato potenziali pericolose corruzioni nei seguenti file\:
macWarnings.moreInformationButton = Più informazioni
macWarnings.whitelistButton = Decripta i selezionati comunque
# settings.fxml
settings.version.label = Versione %s
settings.checkForUpdates.label = Verifica aggiornamenti
settings.port.label = WebDAV Port *
settings.port.prompt = 0 \= Scegli automaticamente
settings.useipv6.label = Utilizza IPv6 literal
settings.requiresRestartLabel = * Cryptomator deve essere riavviato
# tray icon
tray.menu.open = Apri
tray.menu.quit = Chiudi
tray.infoMsg.title = Ancora in esecuzione
tray.infoMsg.msg = Cryptomator è ancora in esecuzione. Chiudilo utilizzando l'icona nel menù di stato.
tray.infoMsg.msg.osx = Cryptomator è ancora in esecuzione. Chiudilo utilizzando l'icona nella barra del menù.

View File

@@ -0,0 +1,84 @@
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# ChangHwan Kim
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = 여기를 클릭하여 보관함 추가하기
main.directoryList.contextMenu.remove = 목록에서 삭제
main.directoryList.contextMenu.changePassword = 비밀번호 변경
main.addDirectory.contextMenu.new = 새 보관함 만들기
main.addDirectory.contextMenu.open = 기존 보관함 열기
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = 업데이트 확인
welcome.newVersionMessage = %s 버전이 새로 다운로드 가능합니다. 지금 버전은 %s 입니다.
# initialize.fxml
initialize.label.password = 비밀번호
initialize.label.retypePassword = 비밀번호 재입력
initialize.button.ok = 보관함 만들기
initialize.messageLabel.alreadyInitialized = 이미 보관함이 초기화되었습니다.
initialize.messageLabel.initializationFailed = 보관함을 초기화할 수 없습니다. 자세한 사항은 로그 파일을 참조하세요.
# notfound.fxml
notfound.label = 보관함을 찾을 수 없습니다. 옮겨진 것은 아닌가요?
# upgrade.fxml
upgrade.button = 보관함 업그레이드
upgrade.version3dropBundleExtension.msg = 이 보관함은 새로운 형식으로 다시 바뀔 필요가 있습니다. "%1$s"의 이름은 "%2$s"로 바뀔 것입니다. 진행하기 전에 동기화가 완료되었는지 다시 한 번 확인해주시기 바랍니다.
upgrade.version3dropBundleExtension.err.alreadyExists = 자동 마이그레이션 실패. "%s"가 이미 존재합니다.
# unlock.fxml
unlock.label.password = 비밀번호
unlock.label.mountName = 드라이브 이름
unlock.label.winDriveLetter = 드라이브 문자
unlock.label.downloadsPageLink = 모든 Cryptomator 버전
unlock.label.advancedHeading = 고급 옵션
unlock.button.unlock = 보관함 해제
unlock.button.advancedOptions.show = 더 많은 옵션
unlock.button.advancedOptions.hide = 기본 옵션
unlock.choicebox.winDriveLetter.auto = 자동으로 할당
unlock.errorMessage.wrongPassword = 틀린 비밀번호
unlock.errorMessage.mountingFailed = 마운트 실패. 자세한 사항은 로그 파일을 참조하세요.
unlock.errorMessage.unsupportedKeyLengthInstallJCE = 복호화 실패. Oracle JCE Unlimited Strength Policy Files을 설치하세요.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = 지원되지 않는 보관함. 이 보관함은 이전 버전의 Cryptomator에서 생성되었습니다.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = 지원되지 않는 보관함. 이 보관함은 최신 버전의 Cryptomator에서 생성되었습니다.
unlock.messageLabel.startServerFailed = WedDAV 서버 시작 실패
# change_password.fxml
changePassword.label.oldPassword = 이전 비밀번호
changePassword.label.newPassword = 새로운 비밀번호
changePassword.label.retypePassword = 비밀번호 재입력
changePassword.label.downloadsPageLink = 모든 Cryptomator 버전
changePassword.button.change = 비밀번호 변경
changePassword.errorMessage.wrongPassword = 틀린 비밀번호
changePassword.errorMessage.decryptionFailed = 복호화 실패
changePassword.errorMessage.unsupportedKeyLengthInstallJCE = 복호화 실패. Oracle JCE Unlimited Strength Policy Files을 설치하세요.
changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware = 지원되지 않는 보관함. 이 보관함은 이전 버전의 Cryptomator에서 생성되었습니다.
changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault = 지원되지 않는 보관함. 이 보관함은 최신 버전의 Cryptomator에서 생성되었습니다.
changePassword.infoMessage.success = 비밀번호 변경
# unlocked.fxml
unlocked.button.lock = 보관함 잠그기
unlocked.moreOptions.reveal = 드라이브 표시
unlocked.moreOptions.copyUrl = WebDAV 주소 복사
unlocked.label.revealFailed = 명령 실패
unlocked.label.unmountFailed = 드라이브 추출 실패
unlocked.label.statsEncrypted = 암호화
unlocked.label.statsDecrypted = 복호화
unlocked.ioGraph.yAxis.label = 처리량 (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = 위험 - %s에 손상된 파일
macWarnings.message = Cryptomator가 다음 파일들에서 잠재적인 손상 위험을 감지했습니다.
macWarnings.moreInformationButton = 더 알아보기
macWarnings.whitelistButton = 선택 항목 강제 복호화
# settings.fxml
settings.version.label = 버전 %s
settings.checkForUpdates.label = 업데이트 확인
settings.port.label = WebDAV 포트 *
settings.port.prompt = 0 \= 자동으로 선택
settings.useipv6.label = IPv6 사용
settings.requiresRestartLabel = * Cryptomator 재시작 필요
# tray icon
tray.menu.open = 열기
tray.menu.quit = 종료
tray.infoMsg.title = 계속 실행 중입니다.
tray.infoMsg.msg = Cryptomator가 계속 실행 중입니다. 종료하실려면 트레이 아이콘에서 해주세요.
tray.infoMsg.msg.osx = Cryptomator가 계속 실행중입니다. 종료하실려면 메뉴 바 아이콘에서 해주세요.

View File

@@ -0,0 +1,34 @@
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# Will
# André Di Biasi
app.name = Cryptomator
main.directoryList.contextMenu.remove = Remover da lista
main.directoryList.contextMenu.changePassword = Alterar senha
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Procurando atualizações...
# initialize.fxml
initialize.label.password = Senha
initialize.label.retypePassword = Digite a senha novamente
# unlock.fxml
unlock.label.password = Senha
unlock.label.advancedHeading = Opções avançadas
unlock.button.advancedOptions.hide = Menos opções
unlock.choicebox.winDriveLetter.auto = Atribuir automaticamente
unlock.errorMessage.wrongPassword = Senha incorreta
unlock.errorMessage.mountingFailed = Montagem falhou. Veja o arquivo de log para detalhes
unlock.messageLabel.startServerFailed = Inicialização do servidor WEBDAV falhou
# change_password.fxml
changePassword.label.oldPassword = Senha antiga
changePassword.label.newPassword = Nova senha
changePassword.label.retypePassword = Digite a senha novamente
changePassword.button.change = Alterar senha
changePassword.infoMessage.success = Senha alterada
unlocked.label.revealFailed = Falha no comando
unlocked.label.unmountFailed = Falha ao ejetar o drive
unlocked.label.statsEncrypted = encriptado
macWarnings.moreInformationButton = Saiba mais

View File

@@ -0,0 +1,55 @@
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# Garik
# Konstantine
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Нажмите здесь что добавить хранилище
main.directoryList.contextMenu.remove = Удалить из списка
main.directoryList.contextMenu.changePassword = Сменить пароль
main.addDirectory.contextMenu.new = Создать новое хранилище
main.addDirectory.contextMenu.open = Открыть существующее хранилище
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Проверка обновлений...
welcome.newVersionMessage = Версия может быть скачена. Это.
# initialize.fxml
initialize.label.password = Пароль
initialize.label.retypePassword = Повторите пароль
initialize.button.ok = Создать хранилище
initialize.messageLabel.alreadyInitialized = Хранилище уже инициализировано
initialize.messageLabel.initializationFailed = Невозможно инициировать хранилище. Смотрите лог для деталей.
# notfound.fxml
notfound.label = Хранилище не найдено.Оно было удалено?
# upgrade.fxml
upgrade.button = Обновить хранилище
upgrade.version3dropBundleExtension.msg = Этому хранилищу нужно мигрировать в новый формат.\n"%1$s" will be renamed to "%2$s".\nPlease make sure synchronization has finished before proceeding.
upgrade.version3dropBundleExtension.err.alreadyExists = Автоматическая миграция не удалась.\n"%" уже существует.
# unlock.fxml
unlock.label.password = Пароль
unlock.label.mountName = Имя носителя
changePassword.errorMessage.wrongPassword = Неправильный пароль
changePassword.errorMessage.decryptionFailed = Расшифровка провалилась
changePassword.errorMessage.unsupportedKeyLengthInstallJCE = Расшифровка не удалась. Пожалуйста установите Oracle JCE Unlimited Strength Policy.
changePassword.infoMessage.success = Пароль изменился
# unlocked.fxml
unlocked.button.lock = Заблокировать хранилище
unlocked.moreOptions.copyUrl = Скопировать WebDAV URL
unlocked.label.revealFailed = Команда не удалась
unlocked.label.unmountFailed = Извлечение диска не удалось
unlocked.label.statsEncrypted = зашифровано
unlocked.label.statsDecrypted = расшифровано
macWarnings.moreInformationButton = Изучить больше
# settings.fxml
settings.version.label = Версия %s
settings.checkForUpdates.label = Проверка обновлений
settings.requiresRestartLabel = * Cryptomator должен перезагрузится
# tray icon
tray.menu.open = Открыть
tray.menu.quit = Выйти
tray.infoMsg.title = Всё ещё работает
tray.infoMsg.msg = Cryptomator работает. Выйдите c помощью иконки в трее.
tray.infoMsg.msg.osx = Cryptomator всё ещё работает.Выйдите с помощью иконки в меню баре.

View File

@@ -0,0 +1,88 @@
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# Filip Havrlent
# Tatiana Chovancová
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Pridať trezor
main.directoryList.contextMenu.remove = Odstrániť zo zoznamu
main.directoryList.contextMenu.changePassword = Zmeniť heslo
main.addDirectory.contextMenu.new = Vytvoriť nový trezor
main.addDirectory.contextMenu.open = Otvoriť existujúci trezor
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Kontrolujú sa aktualizácie...
welcome.newVersionMessage = Verzia %s je pripravená na stiahnutie. Toto je verzia %s.
# initialize.fxml
initialize.label.password = Heslo
initialize.label.retypePassword = Zadajte heslo znova
initialize.button.ok = Vytvoriť trezor
initialize.messageLabel.alreadyInitialized = Trezor je už inicializovaný
initialize.messageLabel.initializationFailed = Nepodarilo sa inicializovať trezor. Pozrite súbor záznamov pre viac detailov.
# notfound.fxml
notfound.label = Trezor nemohol byť nenájdený. Bol presunutý?
# upgrade.fxml
upgrade.button = Upgradnúť trezor
upgrade.version3dropBundleExtension.msg = Tento trezor musí byť premigrovaný na nový formát. "%1$s" bude premenovaný na "%2$s". Prosím, uistite sa že je dokončená synchronizácia skôr než budete pokračovať.
upgrade.version3dropBundleExtension.err.alreadyExists = Automatická migrácia zlyhala. "%s" už existuje.
# unlock.fxml
unlock.label.password = Heslo
unlock.label.mountName = Názov jednotky
unlock.label.winDriveLetter = Označenie jednotky
unlock.label.downloadsPageLink = Všetky verzie Cryptomatoru
unlock.label.advancedHeading = Pokročilé nastavenia
unlock.button.unlock = Odomknúť trezor
unlock.button.advancedOptions.show = Viac nastavení
unlock.button.advancedOptions.hide = Menej nastavení
unlock.choicebox.winDriveLetter.auto = Priradiť automaticky
unlock.errorMessage.wrongPassword = Nesprávne heslo
unlock.errorMessage.mountingFailed = Pripájanie zlyhalo. Viac informácii v logu.
unlock.errorMessage.unsupportedKeyLengthInstallJCE = Dešifrovanie zlyhalo. Prosím nainštalujte Oracle JCE Unlimited Strength Policy Files.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Nepodporovaný trezor. Tento trezor bol vytvorený staršou verziou Cryptromatoru.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Nepodporovaný trezor. Bol vytvorený z novšou verziou Cryptomatoru.
unlock.messageLabel.startServerFailed = Spustenie WebDAv servera zlyhalo.
# change_password.fxml
changePassword.label.oldPassword = Staré heslo
changePassword.label.newPassword = Nové heslo
changePassword.label.retypePassword = Znova zadajte heslo
changePassword.label.downloadsPageLink = Všetky verzie Cryptomatoru.
changePassword.button.change = Zmeniť heslo
changePassword.errorMessage.wrongPassword = Nesprávne heslo
changePassword.errorMessage.decryptionFailed = Dešifrovanie zlyhalo.
changePassword.errorMessage.unsupportedKeyLengthInstallJCE = Dešifrovanie zlyhalo. Prosím nainštalujte Oracle JCE Unlimited Strength Policy.
changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Nepodporovaný trezor. Bol vytvorený staršou verziou Cryptomatoru.
changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault = Nepodporovaný trezor. Bol vytvorený novšou verziou Cryptomatoru.
changePassword.infoMessage.success = Heslo zmenené
# unlocked.fxml
unlocked.button.lock = Zamknúť trezor
unlocked.moreOptions.reveal = Odhaliť jednotku
unlocked.moreOptions.copyUrl = Kopírovať WebDAV URL
unlocked.label.revealFailed = Príkaz zlyhal
unlocked.label.unmountFailed = Odpájanie jednotky zlyhalo
unlocked.label.statsEncrypted = zašifrované
unlocked.label.statsDecrypted = dešifrované
unlocked.ioGraph.yAxis.label = Priepustnosť (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = Upozornenie - Poškodený súbor v %s
macWarnings.message = Cryptomator odhalil potencionálne škodlivé poškodenie v nasledujúcich súboroch\:
macWarnings.moreInformationButton = Zistiť viac
macWarnings.whitelistButton = Napriek tomu dešifrovať vybrané
# settings.fxml
settings.version.label = Verzia %s
settings.checkForUpdates.label = Skontrolovať aktualizácie
settings.port.label = WebDAV Port *
settings.port.prompt = 0 \= Vybrať automaticky
settings.useipv6.label = Použiť IPv6
settings.requiresRestartLabel = * Cryptomator vyžaduje reštart
# tray icon
tray.menu.open = Otvoriť
tray.menu.quit = Vypnúť
tray.infoMsg.title = Stále beží
tray.infoMsg.msg = Cryptomator je stále spustený. Vypnite ho pomocou ikony v systémovej lište.
tray.infoMsg.msg.osx = Cryptomator je stále sputený. Ukončite ho pomocou ikony v menu.

View File

@@ -1,89 +0,0 @@
#-------------------------------------------------------------------------------
# Copyright (c) 2016 Markus Kreusch
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# Markus Kreusch - initial API and implementation
#-------------------------------------------------------------------------------
app.name=Cryptomator
# main.fxml
main.emptyListInstructions=Klicken Sie hier, um neue Tresore hinzuzufügen
main.directoryList.contextMenu.remove=Aus Liste entfernen
main.directoryList.contextMenu.changePassword=Passwort ändern
main.addDirectory.contextMenu.new=Tresor erstellen
main.addDirectory.contextMenu.open=Tresor öffnen
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking=Auf Updates prüfen...
welcome.newVersionMessage=Version %s kann heruntergeladen werden. Aktuelle Version %s.
# initialize.fxml
initialize.label.password=Passwort
initialize.label.retypePassword=Passwort bestätigen
initialize.button.ok=Tresor erstellen
initialize.messageLabel.alreadyInitialized=Tresor bereits vorhanden
initialize.messageLabel.initializationFailed=Fehler beim Initialisieren. Details in der Log-Datei.
# unlock.fxml
unlock.label.password=Passwort
unlock.label.mountName=Laufwerksname
unlock.label.winDriveLetter=Laufwerksbuchstabe
unlock.label.downloadsPageLink=Alle Cryptomator Versionen
unlock.label.advancedHeading=Erweiterte Optionen
unlock.button.unlock=Tresor entsperren
unlock.button.advancedOptions.show=Weitere Optionen
unlock.button.advancedOptions.hide=Weniger Optionen
unlock.choicebox.winDriveLetter.auto=Automatisch ermitteln
unlock.errorMessage.wrongPassword=Falsches Passwort
unlock.errorMessage.mountingFailed=Verbindung fehlgeschlagen. Details in der Log-Datei.
unlock.errorMessage.unsupportedKeyLengthInstallJCE=Entschlüsselung fehlgeschlagen. Bitte die Oracle JCE Unlimited Strength Policy installieren.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware=Tresor nicht unterstützt. Der Tresor wurde mit einer älteren Version von Cryptomator erstellt.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault=Tresor nicht unterstützt. Der Tresor wurde mit einer neueren Version von Cryptomator erstellt.
unlock.messageLabel.startServerFailed=Starten des WebDAV-Servers fehlgeschlagen.
# change_password.fxml
changePassword.label.oldPassword=Altes Passwort
changePassword.label.newPassword=Neues Passwort
changePassword.label.retypePassword=Passwort bestätigen
changePassword.label.downloadsPageLink=Alle Cryptomator Versionen
changePassword.button.change=Passwort ändern
changePassword.errorMessage.wrongPassword=Falsches Passwort
changePassword.errorMessage.decryptionFailed=Entschlüsselung fehlgeschlagen
changePassword.errorMessage.unsupportedKeyLengthInstallJCE=Entschlüsselung fehlgeschlagen. Bitte die Oracle JCE Unlimited Strength Policy installieren.
changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware=Tresor nicht unterstützt. Der Tresor wurde mit einer älteren Version von Cryptomator erstellt.
changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault=Tresor nicht unterstützt. Der Tresor wurde mit einer neueren Version von Cryptomator erstellt.
changePassword.infoMessage.success=Passwort geändert
# unlocked.fxml
unlocked.button.lock=Tresor sperren
unlocked.moreOptions.reveal=Laufwerk anzeigen
unlocked.moreOptions.copyUrl=WebDAV-URL kopieren
unlocked.label.revealFailed=Befehl fehlgeschlagen
unlocked.label.unmountFailed=Trennen des Laufwerks fehlgeschlagen
unlocked.label.statsEncrypted=verschlüsselt
unlocked.label.statsDecrypted=entschlüsselt
unlocked.ioGraph.yAxis.label=Durchsatz (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle=Achtung - Kompromittierte Datei in %s
macWarnings.message=Cryptomator hat möglicherweise unerlaubte Veränderungen in den folgenden Dateien erkannt:
macWarnings.moreInformationButton=Mehr erfahren
macWarnings.whitelistButton=Trotzdem entschlüsseln
# settings.fxml
settings.version.label=Version %s
settings.checkForUpdates.label=Auf Updates prüfen
settings.port.label=WebDAV Port *
settings.port.prompt=0 = Automatisch wählen
settings.useipv6.label=IPv6-Literal nutzen
settings.requiresRestartLabel=* benötigt Neustart von Cryptomator
# tray icon
tray.menu.open=Öffnen
tray.menu.quit=Beenden
tray.infoMsg.title=Cryptomator läuft noch
tray.infoMsg.msg=Cryptomator läuft noch. Mit dem Tray-Icon beenden.
tray.infoMsg.msg.osx=Cryptomator läuft noch. Über die Menüleiste beenden.