Compare commits

...

301 Commits

Author SHA1 Message Date
Sebastian Stenzel
281cfb74d9 Merge branch 'release/1.3.0-rc1'
# Conflicts:
#	main/ant-kit/pom.xml
#	main/commons-test/pom.xml
#	main/commons/pom.xml
#	main/filesystem-charsets/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/jacoco-report/pom.xml
#	main/keychain/pom.xml
#	main/launcher/pom.xml
#	main/pom.xml
#	main/uber-jar/pom.xml
#	main/ui/pom.xml
2017-04-20 16:11:37 +02:00
Sebastian Stenzel
0d03eeb5bd Adjusted build config for 1.3.0 2017-04-20 15:02:29 +02:00
Sebastian Stenzel
36e669d729 Preparing 1.3.0-rc1 2017-04-20 14:49:32 +02:00
Sebastian Stenzel
b1f55d7cfe updated version check url [ci skip] 2017-04-19 15:25:51 +02:00
Sebastian Stenzel
9aa8c46560 Updated SemVerComparator to support pre-release versions. 2017-04-19 15:09:30 +02:00
Sebastian Stenzel
2ed00ed02d fixes #435 2017-04-19 14:02:37 +02:00
Sebastian Stenzel
5d56f5beb6 Enhanced error reporting [ci skip] 2017-04-19 00:12:10 +02:00
Sebastian Stenzel
1b937dfb06 renamed variable [ci skip] 2017-04-19 00:07:10 +02:00
Sebastian Stenzel
42be5330fe Refactored FXML loading 2017-04-19 00:06:46 +02:00
Sebastian Stenzel
be8949157f Removed unused classes 2017-04-18 13:46:25 +02:00
Sebastian Stenzel
93b2a4e07a Refactored Cryptomator UI. Extracted Launcher to its own Maven module. 2017-04-18 13:40:59 +02:00
Sebastian Stenzel
ada1195a26 Updated dependencies, which fixes #473 2017-04-04 18:00:48 +02:00
Sebastian Stenzel
93563f68e0 fixes #430 2017-03-17 16:45:20 +01:00
Sebastian Stenzel
3faa0e83cc fixes #387 2017-03-17 13:16:45 +01:00
Sebastian Stenzel
1985e2af72 fixes #452 and fixes #143 2017-03-15 12:13:34 +01:00
Sebastian Stenzel
3f03d36ad6 configurable mount + reveal during unlock, preparation for #452 and #143 (still needs a little refactoring, though) 2017-03-14 20:46:31 +01:00
Sebastian Stenzel
8359deb8eb Merge branch 'feature/cryptofs' into develop 2017-03-06 22:33:55 +01:00
Sebastian Stenzel
e1f2330f95 Updated dependencies 2017-03-06 17:08:05 +01:00
Sebastian Stenzel
87014c4db2 fixed build 2017-03-01 19:04:34 +01:00
Sebastian Stenzel
c9eb9b1938 Dependency cleanup, migrated from commons-httpclient to org.apache.httpcomponents:httpclient 2017-03-01 18:59:37 +01:00
Sebastian Stenzel
28cb2e1301 Merge branch 'develop' into feature/cryptofs
# Conflicts:
#	main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/Scrypt.java
#	main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFileWithRange.java
#	main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/FilesystemResourceFactory.java
#	main/frontend-webdav/src/test/java/org/cryptomator/frontend/webdav/WebDavServerTest.java
2017-02-24 16:51:42 +01:00
Sebastian Stenzel
277999112e Updated dependencies 2017-02-24 16:48:15 +01:00
Sebastian Stenzel
a7fdf3d325 Merge pull request #454 from jordanbtucker/scrypt-typo
Fix typo in Scrypt Javadoc: `costParam` -> `blockSize`
2017-02-14 10:48:40 +01:00
Jordan Tucker
54ae332364 Fix typo in Scrypt Javadoc: costParam -> blockSize
> Cost parameter `N`, larger than 1, a power of 2 and
> less than `2^(128 * costParam / 8)`

didn't make any sense. It was meant to be

> Cost parameter `N`, larger than 1, a power of 2 and
> less than `2^(128 * blockSize / 8)`
2017-02-13 22:28:07 -08:00
Tobias Hagemann
76075ffec4 Update CONTRIBUTING.md 2017-02-10 01:52:13 +01:00
Tobias Hagemann
ed448146f7 Update CONTRIBUTING.md 2017-02-10 01:50:20 +01:00
Tobias Hagemann
638e9c1a8b Update ISSUE_TEMPLATE.md 2017-02-10 01:37:21 +01:00
Markus Kreusch
1624cffb2a Update README.md 2017-02-02 17:34:39 +01:00
Sebastian Stenzel
f391a6521d automatically save settings when chaning vault properties. fixes #446 2017-02-02 11:31:00 +01:00
Sebastian Stenzel
02ae2e7ca0 Vastly refactored settings, integrated new webdav-nio-adapter snapshot version, allow reconfiguration of IPv6 and Port settings during runtime 2017-01-31 22:30:44 +01:00
Tobias Hagemann
40bd84a09c updated contribution guide 2017-01-30 21:22:10 +01:00
Tobias Hagemann
0c0fb1c4c5 Merge branch 'release/1.2.4' 2017-01-27 11:22:14 +01:00
Tobias Hagemann
ab39bd1667 updated to version 1.2.4 2017-01-27 11:12:45 +01:00
Tobias Hagemann
b41636a208 added japanese localization, updated other localizations 2017-01-26 16:01:49 +01:00
Tobias Hagemann
0902de821a fixed unit test 2017-01-25 17:32:50 +01:00
Tobias Hagemann
8a6265658e fixed range requests 2017-01-25 15:48:21 +01:00
Sebastian Stenzel
7750a49e65 specifying masterkey filename as introduced in cryptofs 1.1.0 2017-01-20 14:01:39 +01:00
Sebastian Stenzel
b75b9781c1 implemented “change password” etc 2017-01-02 17:29:43 +01:00
Sebastian Stenzel
2687c02e31 now passing unchecked CryptoExceptions up through various closures, thus being able to catch “InvalidPassphraseException” in UI 2016-12-21 17:29:54 +01:00
Sebastian Stenzel
71b65e03d6 implemented unlock/reveal/lock 2016-12-20 20:26:17 +01:00
Sebastian Stenzel
e09ee27219 connected filesystem stats 2016-12-20 10:45:40 +01:00
Sebastian Stenzel
5a3428d9b0 cleanup 2016-12-19 17:21:33 +01:00
Sebastian Stenzel
ed109977f8 removed mac warning window fxml 2016-12-19 13:57:59 +01:00
Sebastian Stenzel
d6c6f177e8 code simplification 2016-12-19 13:57:30 +01:00
Sebastian Stenzel
97f2cee1ae mount name now included in servlet path 2016-12-19 13:57:14 +01:00
Sebastian Stenzel
4fb8a27a78 removed MAC warning screen 2016-12-19 13:55:39 +01:00
Sebastian Stenzel
79b825aaad call me THE DESTROYER!!!
first compile-clean but totally fubar version
2016-12-16 17:20:56 +01:00
Sebastian Stenzel
28fedafb59 added linux-launcher-* to RPM [ci skip] 2016-12-12 11:39:23 +01:00
Tobias Hagemann
d9bff68555 updated localization stuff
- added title text to upgrade strategy
- added texts for upgrade 4 to 5
- changed most texts to title style capitalization
2016-11-30 17:22:29 +01:00
Tobias Hagemann
cef3a5fc77 Merge branch 'release/1.2.3'
# Conflicts:
#	main/ant-kit/pom.xml
#	main/commons-test/pom.xml
#	main/commons/pom.xml
#	main/filesystem-api/pom.xml
#	main/filesystem-charsets/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/jacoco-report/pom.xml
#	main/keychain/pom.xml
#	main/pom.xml
#	main/uber-jar/pom.xml
#	main/ui/pom.xml
2016-11-29 23:03:26 +01:00
Tobias Hagemann
9956f43fd9 updated to version 1.2.3 2016-11-29 22:24:26 +01:00
Tobias Hagemann
2b84593bde updated localization 2016-11-29 22:16:46 +01:00
Markus Kreusch
4e728fd387 Merge branch 'feature/issue-363' into develop 2016-11-29 15:21:48 +01:00
Tobias Hagemann
438ade1106 fixes #382 2016-11-27 14:28:44 +01:00
Sebastian Stenzel
fe54f4ec66 Update README.md
As suggested by @tallesh in #135
2016-11-27 10:14:34 +01:00
Markus Kreusch
fe86b4c593 Implemented #363 2016-11-14 22:26:55 +01:00
Markus Kreusch
a583afeb60 Merge branch 'feature/issue-393' into develop 2016-11-14 15:08:31 +01:00
Sebastian Stenzel
a585d3cf16 cherry picked from bac1d6f [ci skip] 2016-11-12 17:04:58 +01:00
Sebastian Stenzel
3db757193e Merge branch 'hotfix/1.2.2' 2016-11-12 17:02:04 +01:00
Sebastian Stenzel
bac1d6fd83 Updated siv-mode to 1.2.0 to be consistent with CryptoLib 2016-11-12 16:41:26 +01:00
Sebastian Stenzel
39ee8a9cde coverity issue 151831 2016-11-11 17:11:21 +01:00
Markus Kreusch
1263b3af81 fixed 'a' really bad thing in the last commit 2016-11-11 09:56:45 +01:00
Markus Kreusch
dafa29d8a3 Implemented #393 2016-11-10 22:58:45 +01:00
Sebastian Stenzel
2bc6fe89ad Merge branch 'release/1.2.1' 2016-11-10 15:23:11 +01:00
Sebastian Stenzel
8439216233 Updated version to 1.2.1 2016-11-10 15:13:28 +01:00
Sebastian Stenzel
aab616d184 Updated CryptoLib, hopefully fixes #373 2016-11-10 15:11:04 +01:00
Sebastian Stenzel
70c3a38c49 invoking UI methods on UI thread, might solve #351 2016-11-10 14:07:37 +01:00
Sebastian Stenzel
c64294ac3e Added chinese localizations, updated dutch localization 2016-11-10 13:41:09 +01:00
Markus Kreusch
82330db871 Additional logging for vault version upgrade 2016-11-09 15:54:10 +01:00
Sebastian Stenzel
c54a721f9a Merge pull request #385 from IAMtheIAM/patch-1
Update README.md
2016-11-06 21:59:16 +01:00
Sebastian Stenzel
355bbb5459 Merge branch 'develop' into patch-1 2016-11-06 21:53:28 +01:00
IAMtheIAM
63daa0f121 Update README.md
Update Readme with accurate info regarding v1.2.0
2016-11-06 03:56:56 -07:00
IAMtheIAM
50885d5c7c Update README.md 2016-11-05 16:51:16 -07:00
IAMtheIAM
4d68818ec5 Update README.md
Update features
2016-11-05 14:00:32 -07:00
IAMtheIAM
6fb20dd509 Update README.md
Added info about obfuscating file size and folder structure (two very important features that should be known!)
2016-11-05 13:54:39 -07:00
Sebastian Stenzel
2bb87dfa96 Merge branch 'release/1.2.0' into develop
# Conflicts:
#	main/ant-kit/pom.xml
#	main/commons-test/pom.xml
#	main/commons/pom.xml
#	main/filesystem-api/pom.xml
#	main/filesystem-charsets/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/jacoco-report/pom.xml
#	main/keychain/pom.xml
#	main/pom.xml
#	main/uber-jar/pom.xml
#	main/ui/pom.xml
2016-09-19 15:12:24 +02:00
Sebastian Stenzel
3e374a927c Merge branch 'release/1.2.0'
# Conflicts:
#	main/ant-kit/pom.xml
#	main/commons-test/pom.xml
#	main/commons/pom.xml
#	main/filesystem-api/pom.xml
#	main/filesystem-charsets/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/jacoco-report/pom.xml
#	main/pom.xml
#	main/uber-jar/pom.xml
#	main/ui/pom.xml
2016-09-19 15:10:43 +02:00
Sebastian Stenzel
84ac6d88f5 added new localization files to unit test [ci skip] 2016-09-15 23:55:37 +02:00
Sebastian Stenzel
72f6ee6477 updated localizations 2016-09-15 23:52:23 +02:00
Sebastian Stenzel
a3cfcb1131 Reject opening files when former filesize header is != -1 2016-09-15 23:26:13 +02:00
Sebastian Stenzel
d7d8d21ba4 Show warning when trying to migrate a masterkey with invalid version mac 2016-09-15 22:15:21 +02:00
Sebastian Stenzel
ef0425e2b1 fixes coverity issue 147409 2016-09-15 14:15:33 +02:00
Sebastian Stenzel
df1fd6d0b3 fixed coverity issue 72979 2016-09-15 14:15:25 +02:00
Sebastian Stenzel
2fa04d7b7c increased version to 1.3.0-SNAPSHOT
[ci skip]
2016-09-15 13:35:22 +02:00
Sebastian Stenzel
a15acd64c8 set version to 1.2.0 2016-09-15 13:33:37 +02:00
Sebastian Stenzel
5b18eff01a increased cryptolib version to 1.0.2
[ci skip]
2016-09-15 13:28:20 +02:00
Sebastian Stenzel
47133c6f31 fixed change pw function leaving invalid JSON file if length gets shorter due to different encoding or pretty printing etc 2016-09-14 17:22:26 +02:00
Sebastian Stenzel
09ba4f5129 changed to jni lib version 1.0.0 2016-09-13 20:17:30 +02:00
Sebastian Stenzel
20d4047bed changed to cryptolib version 1.0.1 (which includes sources) [ci skip] 2016-09-12 23:16:17 +02:00
Sebastian Stenzel
56b71ef7d9 depends on relase version of cryptolib 1.0.0 2016-09-12 21:28:21 +02:00
Sebastian Stenzel
091e62057d Injecting CryptorProvider into UpgradeStrategies 2016-09-12 13:56:47 +02:00
Sebastian Stenzel
824bd9ea64 just added a comment [ci skip] 2016-09-08 18:41:36 +02:00
Sebastian Stenzel
697a791593 updated travis config 2016-09-08 18:34:28 +02:00
Sebastian Stenzel
7462a887b3 updated travis config 2016-09-08 18:30:11 +02:00
Sebastian Stenzel
3535e83d7d updated travis config 2016-09-08 18:20:57 +02:00
Sebastian Stenzel
cf0b4accb3 Merge branch 'feature/external-keychain' into develop 2016-09-04 16:21:52 +02:00
Sebastian Stenzel
a63bcfbaa2 relaxed "vault not empty" check 2016-09-04 16:04:16 +02:00
Sebastian Stenzel
5c4bf2a207 support home-relative paths for cryptomator.keychainPath 2016-09-04 12:27:23 +02:00
Sebastian Stenzel
c1611a12ed implemented Windows keychain 2016-09-03 23:04:53 +02:00
Markus Kreusch
0983120712 Removed Syso logging 2016-09-02 19:21:54 +02:00
Sebastian Stenzel
ce12af8495 Added save password functionality to UI 2016-09-02 15:49:09 +02:00
Sebastian Stenzel
dc117c8415 oracle-java8-unlimited-jce-policy apparently no longer needed (already installed) 2016-08-31 20:12:49 +02:00
Sebastian Stenzel
06e526a961 Merge branch 'develop' into feature/external-keychain
# Conflicts:
#	main/pom.xml
2016-08-31 19:45:15 +02:00
Sebastian Stenzel
2e343a951f Feature/travis container builds (#334)
improved build dependency caching + force updates of snapshots to bypass said cache
2016-08-31 19:39:55 +02:00
Sebastian Stenzel
141ffcf656 Merge branch 'feature/native-functions' into feature/external-keychain 2016-08-31 10:41:51 +02:00
Tobias Hagemann
d61e5c5a08 added "delete passphrase" method to keychain access 2016-08-31 01:08:58 +02:00
Tobias Hagemann
6a15fa132a app launches as foreground app on mac 2016-08-30 22:41:01 +02:00
Sebastian Stenzel
902b29ee0a Merge branch 'develop' into feature/external-keychain
# Conflicts:
#	main/pom.xml
#	main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java
2016-08-30 19:22:45 +02:00
Sebastian Stenzel
995bba616f cache maven dir 2016-08-30 19:15:26 +02:00
Sebastian Stenzel
f39b7b047f Merge branch 'feature/vaultVersion5' into develop 2016-08-30 19:14:28 +02:00
Sebastian Stenzel
72e52df4e0 implemented keychain access on OS X 2016-08-30 19:12:20 +02:00
Sebastian Stenzel
8018e9485e Merge branch 'feature/native-functions' into feature/external-keychain 2016-08-30 19:05:25 +02:00
Sebastian Stenzel
e0ae50378f externalized JNI bindings 2016-08-30 17:19:45 +02:00
Markus Kreusch
a9c2b0fc57 fixes #332 2016-08-29 21:08:58 +02:00
Sebastian Stenzel
dc58ba434a Make Cryptomator a foreground app when restoring from status bar icon 2016-08-29 20:14:48 +02:00
Sebastian Stenzel
34af306309 defined keychain access interfaces 2016-08-29 17:16:56 +02:00
Sebastian Stenzel
21d70b5ae4 moved from coveralls to codecov 2016-08-26 12:52:44 +02:00
Sebastian Stenzel
e90880ac9a speedboost 3000 2016-08-24 17:27:36 +02:00
Sebastian Stenzel
66faa13f40 unlock version 5 vaults 2016-08-23 21:35:13 +02:00
Sebastian Stenzel
8a4a29b4d1 added version 4 to 5 migrator 2016-08-23 21:15:52 +02:00
Sebastian Stenzel
8c8db84a4a refactored migration (using cryptolib) 2016-08-23 21:15:40 +02:00
Sebastian Stenzel
a499a3c80b Merge pull request #324 from oparoz/patch-1
Add that the solution works with Open Source clouds
2016-08-19 19:25:43 +02:00
Olivier Paroz
6a3ccf2b48 Add that the solution works with WebDAV clouds
I think it would be nice to promote other Open Source solutions which use standards such as WebDAV and work well with Cryptomator.
2016-08-19 19:07:39 +02:00
Tobias Hagemann
fcfcffe9cb updated tray icons for OS X [ci skip] 2016-08-19 16:56:04 +02:00
Sebastian Stenzel
363ed4ac4b Accept paths beginning with "~" in cryptomator.settingsPath JVM arg.
[ci skip]
2016-08-17 18:52:08 +02:00
Tobias Hagemann
1f73a08e09 added confirmation checkbox to upgrade screen [ci skip] 2016-08-17 18:11:27 +02:00
Sebastian Stenzel
fe0a34907f Simplified settings/log file path handling. Removed support for %appdata%. Use ~/AppData/Roaming instead! 2016-08-17 15:59:36 +02:00
Sebastian Stenzel
461b11700f added new upgrade log path setting to build script [ci skip] 2016-08-16 19:15:23 +02:00
Sebastian Stenzel
24bfbb59a4 fixes #310 2016-08-16 19:07:05 +02:00
Sebastian Stenzel
4476558e9c fixes #321 2016-08-16 12:33:15 +02:00
Sebastian Stenzel
560171832c Merge branch 'release/1.1.4'
Fixes #308, fixes #319, fixes #318, fixes #317, fixes #311, fixes #267

# Conflicts:
#	main/ant-kit/pom.xml
#	main/commons-test/pom.xml
#	main/commons/pom.xml
#	main/filesystem-api/pom.xml
#	main/filesystem-charsets/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/jacoco-report/pom.xml
#	main/pom.xml
#	main/uber-jar/pom.xml
#	main/ui/pom.xml
2016-08-14 15:12:05 +02:00
Sebastian Stenzel
6e93d40e51 changed version to 1.1.4 2016-08-14 15:06:16 +02:00
Sebastian Stenzel
79b819bca6 Merge branch 'release/1.1.4' into develop 2016-08-14 15:04:44 +02:00
Sebastian Stenzel
a18c406cf0 fixed ConflictResolver 2016-08-14 14:56:44 +02:00
Sebastian Stenzel
6730a83cac fixes coverity issue 141842 2016-08-14 14:45:38 +02:00
Sebastian Stenzel
3b3ebd2196 fixes coverity issue 141838 2016-08-14 14:39:05 +02:00
Sebastian Stenzel
505b6542c7 fixes coverity issue 141844 2016-08-14 14:30:48 +02:00
Sebastian Stenzel
31368f0cba fixes coverity issue 141848 2016-08-14 14:28:52 +02:00
Sebastian Stenzel
5b5dd756b1 fixes coverity issue 141858 2016-08-14 14:27:48 +02:00
Sebastian Stenzel
f6ebbb23d1 fixes coverity issue 141860 2016-08-14 14:16:59 +02:00
Sebastian Stenzel
3f0373b08f removed xdg-utils dependencies, using gvfs-open instead of xdg-open. 2016-08-14 13:57:52 +02:00
Sebastian Stenzel
4c3c60060d Graceful unmounting on Windows and improved error handling of deferred closables. 2016-08-14 13:55:51 +02:00
Markus Kreusch
28f275c22d Requests on parent folders of valid vault urls no longer get delayed 2016-08-12 15:11:54 +02:00
Markus Kreusch
24df3c3809 GvfsMounters now use correct protocol. 2016-08-12 14:11:49 +02:00
Markus Kreusch
034a667e07 Fixed problem with sync conflict resolver. Issue #311 2016-08-11 11:19:12 +02:00
Markus Kreusch
008e3e3b05 Continue mounting also if command fails with error code. Issue #267 2016-08-11 09:39:38 +02:00
Markus Kreusch
94a5bf7596 Continue mounting if reg.exe command fails. Issue #267 2016-08-10 13:57:23 +02:00
Markus Kreusch
e8db836eff Workaround for Issue #317 2016-08-10 13:53:04 +02:00
Markus Kreusch
429b26f3d8 Added delay for requests on invalid vault ids. Issue #319 2016-08-10 13:44:26 +02:00
Markus Kreusch
3ae8327300 Added unique id to vaults / webdav urls.
Issue #319, Issue #308
2016-08-10 13:43:46 +02:00
Markus Kreusch
df7e9a0af1 Preventing post requests. Issue #319 2016-08-09 20:02:06 +02:00
Sebastian Stenzel
93d3eca0ab Yet another header to prevent browsers from guessing mime types. Kudos to @LukasReschke 2016-08-09 17:42:24 +02:00
Sebastian Stenzel
7753d1f0e7 If GET request is made by a browser, the file in question is downloaded instead of being executed. Issue #318 2016-08-09 17:35:17 +02:00
Sebastian Stenzel
d7c6c24932 updated tavis config 2016-08-01 10:21:10 +02:00
Sebastian Stenzel
1a75f23081 Merge branch 'feature/mount-method-settings' into develop 2016-08-01 10:17:44 +02:00
Sebastian Stenzel
f071efe1b9 allow user to specify whether to use dav:// or webdav:// scheme for Linux GVFS mounts. Fixes #307 2016-07-25 10:08:21 +02:00
Sebastian Stenzel
a8ad335aed Update README.md
[ci skip]
2016-07-18 12:18:47 +02:00
Markus Kreusch
7022a80c95 Improved error handling
* Created AsyncTaskService to build async UI operations which always log
uncaught exceptions
* Changed all executor service invocations in the UI to invocations of
AsyncTaskService
* Improved error handling in some other places, especially
try-with-resources
* Unlocking read/write locks in NioFile when opening of a channel fails
2016-07-14 13:58:17 +02:00
Sebastian Stenzel
9a2f602d6c fixes #270 2016-07-13 12:37:40 +02:00
Sebastian Stenzel
c78a4aa241 updated travis config [ci skip] 2016-07-11 22:14:35 +02:00
Tobias Hagemann
975ce4d973 Merge branch 'release/1.1.3'
# Conflicts:
#	main/ant-kit/pom.xml
#	main/commons-test/pom.xml
#	main/commons/pom.xml
#	main/filesystem-api/pom.xml
#	main/filesystem-charsets/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/jacoco-report/pom.xml
#	main/pom.xml
#	main/uber-jar/pom.xml
#	main/ui/pom.xml
2016-07-11 21:16:23 +02:00
Tobias Hagemann
1e6ff0d969 set version to 1.1.3 2016-07-11 21:14:35 +02:00
Sebastian Stenzel
69e133d561 Improved migration from vault version 3 to 4. 2016-07-11 18:07:55 +02:00
Sebastian Stenzel
20e55eddf8 Merge branch 'develop' 2016-07-09 13:25:54 +02:00
Sebastian Stenzel
0fdcdc816a fixed unit test 2016-07-09 13:25:24 +02:00
Sebastian Stenzel
b7506d97a9 Merge branch 'hotfix/1.1.2' 2016-07-09 13:25:03 +02:00
Sebastian Stenzel
4ad7481dc7 fixed unit test 2016-07-09 11:44:53 +02:00
Sebastian Stenzel
bc815405d2 merged from hotfix/1.1.2 [ci skip] 2016-07-09 11:32:02 +02:00
Sebastian Stenzel
9c06e762c3 fixes #304 2016-07-09 11:28:36 +02:00
Sebastian Stenzel
1ac87dd32f fixed NPE [ci skip] 2016-07-08 15:52:00 +02:00
Sebastian Stenzel
e0ce7ce2ec Merge branch 'release/1.1.1'
# Conflicts:
#	main/ant-kit/pom.xml
#	main/commons-test/pom.xml
#	main/commons/pom.xml
#	main/filesystem-api/pom.xml
#	main/filesystem-charsets/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/jacoco-report/pom.xml
#	main/pom.xml
#	main/uber-jar/pom.xml
#	main/ui/pom.xml
2016-07-08 11:59:39 +02:00
Sebastian Stenzel
3d951a9d7b set version to 1.1.1 2016-07-07 14:48:49 +02:00
Sebastian Stenzel
cec3d984b0 Merge branch 'develop' into release/1.1.1 2016-07-07 14:39:20 +02:00
Sebastian Stenzel
392e474cfa Update tr.txt (POEditor.com) 2016-07-07 14:28:52 +02:00
Sebastian Stenzel
41fb0d51a4 Update es.txt (POEditor.com) 2016-07-07 14:28:51 +02:00
Sebastian Stenzel
aa9fef2967 Update sk.txt (POEditor.com) 2016-07-07 14:28:49 +02:00
Sebastian Stenzel
adc9c02564 Update ru.txt (POEditor.com) 2016-07-07 14:28:48 +02:00
Sebastian Stenzel
ace64117a2 Update kr.txt (POEditor.com) 2016-07-07 14:28:46 +02:00
Sebastian Stenzel
fb4db2506b Update it.txt (POEditor.com) 2016-07-07 14:28:45 +02:00
Sebastian Stenzel
1076d971ae Update hu.txt (POEditor.com) 2016-07-07 14:28:43 +02:00
Sebastian Stenzel
eed1b1cff0 Update de.txt (POEditor.com) 2016-07-07 14:28:42 +02:00
Sebastian Stenzel
f5cb82e21e Update fr.txt (POEditor.com) 2016-07-07 14:28:40 +02:00
Sebastian Stenzel
67661f114b Update nl.txt (POEditor.com) 2016-07-07 14:28:39 +02:00
Sebastian Stenzel
8a3e09764a only remove .cryptomator extension for vault version 3 2016-07-07 14:25:55 +02:00
Sebastian Stenzel
eb3cfd6e6a updated placeholders [ci skip] 2016-07-06 16:25:31 +02:00
Sebastian Stenzel
4d1727d0e9 Merge branch 'develop' into release/1.1.1 2016-07-06 16:09:32 +02:00
Sebastian Stenzel
a51d853d1c adjusted number format regex [ci skip] 2016-07-06 16:09:14 +02:00
Sebastian Stenzel
d0039466f7 test technical correctness of localization files 2016-07-06 16:07:07 +02:00
Sebastian Stenzel
5c959989a2 Fixed Coverity defect 131711 2016-07-05 23:18:18 +02:00
Sebastian Stenzel
6283d2df3d Merge branch 'feature/vault-version-4' into develop 2016-07-03 17:41:46 +02:00
Sebastian Stenzel
a9e0dfdaf8 redesigned upgrade view 2016-07-03 17:38:46 +02:00
Sebastian Stenzel
45ca7e9e47 migration from vault version 3 to 4 2016-07-03 16:16:23 +02:00
Sebastian Stenzel
034b5c2718 updated localizations
[ci skip]
2016-07-01 11:14:49 +02:00
Sebastian Stenzel
e188649c79 adjusted test to vault version 4 2016-06-30 22:18:43 +02:00
Sebastian Stenzel
1468c6ec90 improved vault upgrading, preparation for migration to vault version 4 2016-06-30 22:09:45 +02:00
Sebastian Stenzel
07ba4eb537 Using 0 prefix instead of _ suffix to mark directories 2016-06-30 18:02:13 +02:00
Sebastian Stenzel
414bbef1a7 updated key generation 2016-06-10 14:04:55 +02:00
Sebastian Stenzel
e2b94ff6ef updated jacoco dependency 2016-06-08 19:11:56 +02:00
Sebastian Stenzel
41f8a9faca add "allow" response header field 2016-06-08 19:06:06 +02:00
Tobias Hagemann
1d9252e974 updated description of file chooser's extension filter [ci skip] 2016-06-07 01:07:54 +02:00
Sebastian Stenzel
80780eef3c Merge pull request #280 from aeris/fix-l10n-fr
Enhanced fr translation
[ci skip]
2016-05-30 14:27:00 +02:00
Aeris
87ff33956b Enhanced fr translation 2016-05-30 13:45:40 +02:00
Sebastian Stenzel
1804b98f05 trigger coverity scans for only release branches [ci skip] 2016-05-25 15:47:19 +02:00
Sebastian Stenzel
847c6813cc started development of 1.2.0 [ci skip] 2016-05-25 15:37:07 +02:00
Sebastian Stenzel
1dde5ff6e7 release 1.1.0 2016-05-25 15:17:40 +02:00
Sebastian Stenzel
76c9a19428 unset and set default buttons to make sure VK_ENTER triggers it 2016-05-25 12:29:49 +02:00
Sebastian Stenzel
25ee0519e1 some minor fixes
- reset password field contents when changing a vault
- hide "change password" option for uninitialized or missing vaults
2016-05-25 12:12:01 +02:00
Sebastian Stenzel
c184089c35 oopsy daisy, wie das duftet... [ci skip] 2016-05-24 11:36:46 +02:00
Sebastian Stenzel
d2bcc47857 Merge branch 'delete-confirmation'
fixes #228
2016-05-24 11:35:17 +02:00
Sebastian Stenzel
34629a69ea Using ControlsFX's BSD-licensed assets for dialogs, rather than OpenJDK's GPL licensed ones.
Adjusted dialog styles for Linux and Windows.
[ci skip]
2016-05-24 11:32:27 +02:00
Sebastian Stenzel
92c87f7b84 changed dialog L&F on OS X 2016-05-23 19:31:18 +02:00
Sebastian Stenzel
0dd96635ac code cleanup [ci skip] 2016-05-23 13:24:53 +02:00
Sebastian Stenzel
048c44a6e4 Update README.md 2016-05-23 12:36:58 +02:00
Sebastian Stenzel
06910ad1f4 fixes #229 2016-05-23 12:11:45 +02:00
Sebastian Stenzel
02a0f3acc6 fixed invariant FolderChildrenTests 2016-05-23 11:18:24 +02:00
Sebastian Stenzel
851f9240b7 updated link to MAC warning FAQ 2016-05-23 11:02:56 +02:00
Sebastian Stenzel
99fce8d0b7 automatically resolve conflicts for directory files, that contain the same directory ID 2016-05-23 11:02:44 +02:00
Sebastian Stenzel
bf05c59c3b Transparent conflict detection for long file names 2016-05-22 15:16:32 +02:00
Sebastian Stenzel
3dcebb1e1f fixed minor copy/paste error 2016-05-22 13:32:16 +02:00
Sebastian Stenzel
fe3efdf610 Merge pull request #269 from jncharon/master
fixes #56
2016-05-21 14:07:15 +02:00
jncharon
5f4ae46f82 Replaced the MouseListner by a MouseAdapter 2016-05-20 21:30:33 +02:00
jncharon
deef325319 Implementation of github issue #56 2016-05-20 19:29:53 +02:00
jncharon
fbe00a8fe3 Merge remote-tracking branch 'refs/remotes/cryptomator/master' 2016-05-16 14:20:55 +02:00
Sebastian Stenzel
dc87dade43 Merge pull request #259 from jncharon/issue-228-fixed
Fixes #228.
Still need to check license of icons, will discuss this with @MuscleRumble, who has a lot of icons that we bought the license for. If necessary we will change them.
2016-05-16 12:53:53 +02:00
Jean-Noël Charon
ba1625b5ad Merge pull request #2 from overheadhunter/issue-228-fixed
Reverted commit c0f4a2b, added .idea/ to .gitignore
2016-05-16 12:14:38 +02:00
Sebastian Stenzel
f6b126415e added IntelliJ files to .gitignore 2016-05-16 10:00:39 +02:00
Sebastian Stenzel
9147e1c08b Revert "Fucking .idea files I could not remove from the vcs"
This reverts commit c0f4a2b0d3.
2016-05-16 09:57:10 +02:00
Sebastian Stenzel
6c18103662 Remove files with non-decryptable names from dir listings 2016-05-13 18:59:06 +02:00
Sebastian Stenzel
6fc343ea12 more fault-tolerant behaviour when mapping of long filenames couldn't be found. 2016-05-13 14:10:37 +02:00
Sebastian Stenzel
d304d66cdd Updated localizations [ci skip] 2016-05-12 19:23:24 +02:00
Sebastian Stenzel
2ce9143b85 Merge branch 'conflict-detection' 2016-05-12 19:14:48 +02:00
Sebastian Stenzel
1c54e4f4ad in the unlikely event of an alternative name already being used, choose a new random conflict id. 2016-05-12 16:13:03 +02:00
Sebastian Stenzel
9fd6f2ecae transparently show sync conflicts (fixes #98) 2016-05-12 16:08:52 +02:00
Sebastian Stenzel
0d9f8eefc0 Using pattern based filename filtering
This is a preparation for finding valid encrypted names inside filenames that include additional characters
2016-05-12 11:51:14 +02:00
Sebastian Stenzel
40a1530f19 repeated commit 86000ac 2016-05-10 14:52:30 +02:00
Sebastian Stenzel
0477a0a2e3 Merge branch 'patches-1.0.x'
# Conflicts:
#	main/filesystem-charsets/pom.xml
2016-05-10 14:49:20 +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
jncharon
2b01b76926 Merge remote-tracking branch 'refs/remotes/cryptomator/master' 2016-05-08 17:52:14 +02:00
Sebastian Stenzel
dcea9e21f0 added module to code coverage report 2016-05-07 15:00:20 +02:00
Sebastian Stenzel
78645ecdf6 fixes #264 2016-05-07 14:40:44 +02:00
Sebastian Stenzel
91646dd93d Merge branch 'password-strength'
Added password strength meter by Jean-Noël Charon, closing issue #198
2016-05-06 18:59:26 +02:00
jncharon
fca146e939 Merge remote-tracking branch 'remotes/origin/master' into issue-228-fixed 2016-05-05 21:22:28 +02:00
jncharon
62aa3ccc7f Merge remote-tracking branch 'refs/remotes/cryptomator/master' into issue-228-fixed 2016-05-05 21:12:53 +02:00
jncharon
c0f4a2b0d3 Fucking .idea files I could not remove from the vcs 2016-05-05 21:11:57 +02:00
Tobias Hagemann
68ee89af98 updated bot welcome asset [ci skip] 2016-05-03 19:12:21 +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
jncharon
6deb30307e Merge remote-tracking branch 'cryptomator/master' 2016-04-24 13:47:37 +02:00
jncharon
7357829741 Fix in the background color of the dialog boxes 2016-04-23 23:42:28 +02:00
jncharon
4bd04150c1 Implementation of github issue 228 2016-04-23 23:37:56 +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
cf35772c18 Merge pull request #239 from jncharon/master
Fix in the change password screen
2016-04-21 09:11:23 +02:00
jncharon
b0fd226c4c Fix of the strength bar position (row) in the fxml 2016-04-20 22:32:30 +02:00
jncharon
0d188d1c0c Merge remote-tracking branch 'cryptomator/master' 2016-04-16 15:27:01 +02:00
Sebastian Stenzel
c6016ec7b2 using constructor-injection, organized imports, code autoformatting [ci skip] 2016-04-16 14:10:32 +02:00
Sebastian Stenzel
e8719a1f9b Merge pull request #232 from jncharon/master
Fixes #198, #157
2016-04-16 09:53:14 +02:00
jncharon
27baf78029 More refactoring following Sebastian comments 2016-04-16 00:12:59 +02:00
jncharon
bf5ce9a3a5 New password strength implementation based on zxcvbn4j 2016-04-15 22:52:57 +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
jncharon
bcfe040784 Merge remote-tracking branch 'cryptomator/master' 2016-04-13 18:22:16 +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
jncharon
d9b88ad1b7 Merge remote-tracking branch 'refs/remotes/cryptomator/master' 2016-04-12 21:53:44 +02:00
jncharon
e66e5b1d96 Added the password strength indicator in the change password window 2016-04-12 21:27:31 +02:00
jncharon
588166dce9 Added the password strength indicator in the initialize window 2016-04-12 21:00:41 +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
434 changed files with 8108 additions and 26299 deletions

6
.gitignore vendored
View File

@@ -11,3 +11,9 @@
.classpath
target/
test-output/
# IntelliJ Settings Files #
.idea/
out/
.idea_modules/
*.iws

View File

@@ -1,19 +1,30 @@
language: java
sudo: required
dist: trusty
jdk:
- oraclejdk8
cache:
directories:
- $HOME/.m2
env:
global:
- 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"
script: mvn -fmain/pom.xml clean test
after_success: mvn -fmain/pom.xml clean test jacoco:report coveralls:report
- secure: "lV9OwUbHMrMpLUH1CY+Z4puLDdFXytudyPlG1eGRsesdpuG6KM3uQVz6uAtf6lrU8DRbMM/T7ML+PmvQ4UoPPYLdLxESLLBat2qUPOIVBOhTSlCc7I0DmGy04CSvkeMy8dPaQC0ukgNiR7zwoNzfcpGRN/U9S8tziDruuHoZSrg=" #bintray
addons:
coverity_scan:
project:
name: "cryptomator/cryptomator"
notification_email: sebastian.stenzel@cryptomator.org
build_command: "mvn -fmain/pom.xml clean test -DskipTests"
branch_pattern: release.*
install:
# "clean" needed until https://bugs.openjdk.java.net/browse/JDK-8067747 is resolved.
- mvn -fmain/pom.xml clean package -DskipTests dependency:go-offline -Ptest-coverage
- mvn -fmain/pom.xml clean package -DskipTests dependency:go-offline -Prelease
script:
- mvn --update-snapshots -fmain/pom.xml -Ptest-coverage clean test jacoco:report-aggregate
after_success:
- "bash <(curl -s https://codecov.io/bash)"
notifications:
webhooks:
urls:
@@ -26,24 +37,27 @@ notifications:
secure: "lngJ/HEAFBbD5AdiO9avMqptKpZHdmEwOzS9FabZjkdFh7yAYueTk5RniPUvShjsKtThYm7cJ8AtDMDwc07NvPrzbMBRtUJGwuDT+7c7YFALGFJ1NYi+emkC9x1oafvmPgEYSE+tMKzNcwrHi3ytGgKdIotsKwaF35QNXYA9aMs="
on_success: change
on_failure: always
before_deploy: mvn -fmain/pom.xml -Puber-jar clean package -DskipTests
addons:
coverity_scan:
project:
name: "cryptomator/cryptomator"
notification_email: sebastian.stenzel@cryptomator.org
build_command: "mvn -fmain/pom.xml clean test -DskipTests"
branch_pattern: coverity_scan
before_deploy:
- mvn -fmain/pom.xml -Prelease clean package -DskipTests
deploy:
provider: releases
- provider: releases
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
tags: true
- provider: script
script: "curl -X POST -u cryptobot:${BINTRAY_API_KEY} -H 'Content-Type: application/json' -d '{\"name\": \"${TRAVIS_TAG}\", \"vcs_tag\": \"${TRAVIS_TAG}\"}' https://api.bintray.com/packages/cryptomator/cryptomator/cryptomator-win/versions"
on:
repo: cryptomator/cryptomator
tags: true
- provider: script
script: "curl -X POST -u cryptobot:${BINTRAY_API_KEY} -H 'Content-Type: application/json' -d '{\"name\": \"${TRAVIS_TAG}\", \"vcs_tag\": \"${TRAVIS_TAG}\"}' https://api.bintray.com/packages/cryptomator/cryptomator/cryptomator-osx/versions"
on:
repo: cryptomator/cryptomator
tags: true

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/

38
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,38 @@
# 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 and Android app can be reported on the [Cryptomator for iOS issues list](https://github.com/cryptomator/cryptomator-ios/issues) and [Cryptomator for Android issues list](https://github.com/cryptomator/cryptomator-android/issues) respectively.
- Ensure the bug was not [already reported](https://github.com/cryptomator/cryptomator/issues). You can also check out our [knowledge base](https://cryptomator.freshdesk.com/support/solutions) 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).
## Do you miss a feature?
- Ensure the feature was not [already requested](https://github.com/cryptomator/cryptomator/issues).
- You're welcome to suggest a feature by [submitting a new issue](https://github.com/cryptomator/cryptomator/issues/new).
## 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

38
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,38 @@
To tick a checkbox replace [ ] with [x]. Make sure to replace placeholders (…) accordingly.
## Issue Checklist
Before creating a new issue make sure that you
- [ ] searched [existing (and closed) issues](https://github.com/cryptomator/cryptomator/issues).
- [ ] searched the [knowledge base](https://cryptomator.freshdesk.com/support/solutions).
- [ ] have read the [contribution guide](https://github.com/cryptomator/cryptomator/blob/master/CONTRIBUTING.md).
- [ ] have read the [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/master/CODE_OF_CONDUCT.md).
## Basic Info
This is a
- [ ] bug report.
- [ ] feature request.
- [ ] question or something else.
I'm using
- [ ] Windows in version: …
- [ ] macOS in version: …
- [ ] Linux in version: …
I'm running Cryptomator in version: …
(You can check the version in the Cryptomator settings.)
## Description
(Please describe in detail what you did, what you expected, and what really happened.)
## Attachments (optional)
If you want to add the log file or screenshots, please add them as attachments. If your log file seems empty and doesn't show any errors, you may enable the [debug mode](https://cryptomator.freshdesk.com/support/solutions/articles/16000046480) first and reproduce the problem to ensure all important information is contained in there. You may use test data or redact sensitive information from the log file.
You can find the log file
- on Windows: %appdata%/Cryptomator/cryptomator.log
- on macOS: ~/Library/Logs/Cryptomator/cryptomator.log
- on Linux: ~/.Cryptomator/cryptomator.log

View File

@@ -1,44 +1,72 @@
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)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/2a0adf3cec6a4143b91035d3924178f1)](https://www.codacy.com/app/cryptomator/cryptomator?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=cryptomator/cryptomator&amp;utm_campaign=Badge_Grade)
[![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, Nextcloud and any other cloud storage service which 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 ;-)
- File names get encrypted
- Folder structure gets obfuscated
- 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
* Java 8 + JCE unlimited strength policy files (needed for 256-bit keys)
### Dependencies
* Java 8 (min. 8u51, we recommend to use the current version)
* [JCE unlimited strength policy files](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html) (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), [Linux](https://github.com/cryptomator/builder-containers))
### Run Maven
```
cd main
mvn clean install -Prelease
```
An executable jar file will be created inside `main/uber-jar/target`.
## 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

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.3.0-rc1</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>launcher</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>3.0.2</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>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<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,72 @@
<?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.launcher.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="launcher-${project.version}.jar" />
<fx:resources>
<fx:fileset dir="libs" type="jar" includes="*.jar" excludes="launcher-${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 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:property name="cryptomator.upgradeLogPath" value="~/.Cryptomator/upgrade.log" />
<fx:property name="cryptomator.settingsPath" value="~/.Cryptomator/settings.json" />
<fx:property name="cryptomator.ipcPortPath" value="~/.Cryptomator/ipcPort.bin" />
<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="launcher-${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:property name="cryptomator.upgradeLogPath" value="~/.Cryptomator/upgrade.log" />
<fx:property name="cryptomator.settingsPath" value="~/.Cryptomator/settings.json" />
<fx:property name="cryptomator.ipcPortPath" value="~/.Cryptomator/ipcPort.bin" />
<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="launcher-${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>
</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
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

@@ -10,17 +10,26 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.3</version>
<version>1.2.4</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>
@@ -29,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,73 +0,0 @@
package org.cryptomator.common.test;
import static java.nio.file.Files.walkFileTree;
import static java.util.Collections.synchronizedSet;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TempFilesRemovedOnShutdown {
private static final Logger LOG = LoggerFactory.getLogger(TempFilesRemovedOnShutdown.class);
private static final Set<Path> PATHS_TO_REMOVE_ON_SHUTDOWN = synchronizedSet(new HashSet<>());
private static final Thread ON_SHUTDOWN_DELETER = new Thread(TempFilesRemovedOnShutdown::removeAll);
static {
Runtime.getRuntime().addShutdownHook(ON_SHUTDOWN_DELETER);
}
public static Path createTempDirectory(String prefix) throws IOException {
Path path = Files.createTempDirectory(prefix);
PATHS_TO_REMOVE_ON_SHUTDOWN.add(path);
return path;
}
private static void removeAll() {
PATHS_TO_REMOVE_ON_SHUTDOWN.forEach(TempFilesRemovedOnShutdown::remove);
}
private static void remove(Path path) {
try {
tryRemove(path);
} catch (Throwable e) {
LOG.debug("Failed to remove " + path, e);
}
}
private static void tryRemove(Path path) throws IOException {
walkFileTree(path, new FileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
}

View File

@@ -1,27 +0,0 @@
package org.cryptomator.common.test.matcher;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
/**
* Wraps hamcrest contains and containsInAnyOrder matcher factory methods to
* avoid problems due to incorrect / inconsistent handling of generics by
* several java compilers.
*
* @author Markus Kreusch
*/
public class ContainsMatcher {
@SuppressWarnings({ "unchecked" })
@SafeVarargs
public static <T> Matcher<Iterable<? super T>> containsInAnyOrder(Matcher<? extends T>... matchers) {
return Matchers.containsInAnyOrder((Matcher[]) matchers);
}
@SuppressWarnings({ "unchecked" })
@SafeVarargs
public static <T> Matcher<Iterable<? super T>> contains(Matcher<? extends T>... matchers) {
return Matchers.contains((Matcher[]) matchers);
}
}

View File

@@ -1,48 +0,0 @@
package org.cryptomator.common.test.matcher;
import java.util.Optional;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;
public class ExceptionMatcher<T extends Throwable> extends TypeSafeDiagnosingMatcher<T> {
public static <T extends Throwable> ExceptionMatcher<T> ofType(Class<T> exceptionType) {
return new ExceptionMatcher<>(exceptionType);
}
private final Class<T> exceptionType;
private final Optional<Matcher<T>> subMatcher;
private ExceptionMatcher(Class<T> exceptionType) {
super(exceptionType);
this.exceptionType = exceptionType;
this.subMatcher = Optional.empty();
}
private ExceptionMatcher(Class<T> exceptionType, Matcher<T> subMatcher) {
super(exceptionType);
this.exceptionType = exceptionType;
this.subMatcher = Optional.of(subMatcher);
}
@Override
public void describeTo(Description description) {
subMatcher.ifPresent(description::appendDescriptionOf);
}
@Override
protected boolean matchesSafely(T item, Description mismatchDescription) {
if (subMatcher.map(matcher -> !matcher.matches(item)).orElse(false)) {
subMatcher.get().describeMismatch(item, mismatchDescription);
return false;
}
return true;
}
public Matcher<T> withCauseThat(Matcher<? super Throwable> matcher) {
return new ExceptionMatcher<T>(exceptionType, new PropertyMatcher<>(exceptionType, Throwable::getCause, "cause", matcher));
}
}

View File

@@ -1,61 +0,0 @@
package org.cryptomator.common.test.matcher;
import java.util.Optional;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;
public class OptionalMatcher {
public static <T> Matcher<Optional<T>> presentOptionalWithValueThat(Matcher<? super T> valueMatcher) {
return new TypeSafeDiagnosingMatcher<Optional<T>>(Optional.class) {
@Override
public void describeTo(Description description) {
description //
.appendText("a present Optional with a value that ") //
.appendDescriptionOf(valueMatcher);
}
@Override
protected boolean matchesSafely(Optional<T> item, Description mismatchDescription) {
if (item.isPresent()) {
if (valueMatcher.matches(item.get())) {
return true;
} else {
mismatchDescription.appendText("a present Optional with value that ");
valueMatcher.describeMismatch(item, mismatchDescription);
return false;
}
} else {
mismatchDescription.appendText("an empty Optional");
return false;
}
}
};
}
public static <T> Matcher<Optional<T>> emptyOptional() {
return new TypeSafeDiagnosingMatcher<Optional<T>>(Optional.class) {
@Override
public void describeTo(Description description) {
description.appendText("an empty Optional");
}
@Override
protected boolean matchesSafely(Optional<T> item, Description mismatchDescription) {
if (item.isPresent()) {
mismatchDescription.appendText("a present Optional of ").appendValue(item.get());
return false;
} else {
return true;
}
}
};
}
}

View File

@@ -1,55 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Markus Kreusch
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
******************************************************************************/
package org.cryptomator.common.test.matcher;
import java.util.function.Function;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;
public class PropertyMatcher<T, P> extends TypeSafeDiagnosingMatcher<T> {
private final Class<T> expectedType;
private final Function<? super T, P> getter;
private final String name;
private final Matcher<? super P> subMatcher;
public PropertyMatcher(Class<T> type, Function<? super T, P> getter, String name, Matcher<? super P> subMatcher) {
super(type);
this.expectedType = type;
this.getter = getter;
this.name = name;
this.subMatcher = subMatcher;
}
@Override
public void describeTo(Description description) {
description.appendText("a ") //
.appendText(expectedType.getSimpleName()) //
.appendText(" with a ") //
.appendText(name) //
.appendText(" that ") //
.appendDescriptionOf(subMatcher);
}
@Override
protected boolean matchesSafely(T item, Description mismatchDescription) {
P propertyValue = getter.apply(item);
if (subMatcher.matches(propertyValue)) {
return true;
} else {
mismatchDescription.appendText("a ") //
.appendText(expectedType.getSimpleName()) //
.appendText(" with a ") //
.appendText(name) //
.appendText(" that ");
subMatcher.describeMismatch(propertyValue, mismatchDescription);
return false;
}
}
}

View File

@@ -1,61 +0,0 @@
package org.cryptomator.common.test.mockito;
import static java.util.Arrays.asList;
import java.util.function.Consumer;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class Answers {
public static <T> Answer<T> collectParameters(Answer<T> answer, Consumer<?>... parameterConsumers) {
return new Answer<T>() {
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public T answer(InvocationOnMock invocation) throws Throwable {
for (int i = 0; i < invocation.getArguments().length; i++) {
if (parameterConsumers.length > i) {
((Consumer) parameterConsumers[i]).accept(invocation.getArguments()[i]);
}
}
return answer.answer(invocation);
}
};
}
@SafeVarargs
public static <T> Answer<T> consecutiveAnswers(Answer<T>... answers) {
if (answers == null || answers.length == 0) {
throw new IllegalArgumentException("Required at least one answer");
}
if (asList(answers).contains(null)) {
throw new IllegalArgumentException("No answers must be null");
}
return new Answer<T>() {
private int nextIndex = 0;
@Override
public T answer(InvocationOnMock invocation) throws Throwable {
try {
return answers[nextIndex].answer(invocation);
} finally {
nextIndex = (nextIndex + 1) % answers.length;
}
}
};
}
public static <T> Answer<T> value(T value) {
return new Answer<T>() {
@Override
public T answer(InvocationOnMock invocation) throws Throwable {
return value;
}
};
}
}

View File

@@ -10,32 +10,56 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.3</version>
<version>1.3.0-rc1</version>
</parent>
<artifactId>commons</artifactId>
<name>Cryptomator common</name>
<name>Cryptomator Commons</name>
<description>Shared utilities</description>
<dependencies>
<!-- Libs -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>de.bechte.junit</groupId>
<artifactId>junit-hierarchicalcontextrunner</artifactId>
<scope>test</scope>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<groupId>org.fxmisc.easybind</groupId>
<artifactId>easybind</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>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<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

@@ -1,7 +1,7 @@
package org.cryptomator.common;
@FunctionalInterface
public interface ConsumerThrowingException<T, E extends Exception> {
public interface ConsumerThrowingException<T, E extends Throwable> {
void accept(T t) throws E;

View File

@@ -2,6 +2,7 @@ package org.cryptomator.common;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
public final class LazyInitializer {
@@ -9,7 +10,7 @@ public final class LazyInitializer {
}
/**
* Threadsafe lazy initialization pattern as proposed on http://stackoverflow.com/a/30247202/4014509
* Same as {@link #initializeLazily(AtomicReference, SupplierThrowingException, Class)} except that no checked exception may be thrown by the factory function.
*
* @param <T> Type of the value
* @param reference A reference to a maybe not yet initialized value.
@@ -17,17 +18,59 @@ public final class LazyInitializer {
* @return The initialized value
*/
public static <T> T initializeLazily(AtomicReference<T> reference, Supplier<T> factory) {
final T existingInstance = reference.get();
if (existingInstance != null) {
return existingInstance;
SupplierThrowingException<T, RuntimeException> factoryThrowingRuntimeExceptions = () -> factory.get();
return initializeLazily(reference, factoryThrowingRuntimeExceptions, RuntimeException.class);
}
/**
* Threadsafe lazy initialization pattern as proposed on http://stackoverflow.com/a/30247202/4014509
*
* @param <T> Type of the value
* @param <E> Type of the any expected exception that may occur during initialization
* @param reference A reference to a maybe not yet initialized value.
* @param factory A factory providing a value for the reference, if it doesn't exist yet. The factory may be invoked multiple times, but only one result will survive.
* @param exceptionType Expected exception type.
* @return The initialized value
* @throws E Exception thrown by the factory function.
*/
public static <T, E extends Exception> T initializeLazily(AtomicReference<T> reference, SupplierThrowingException<T, E> factory, Class<E> exceptionType) throws E {
final T existing = reference.get();
if (existing != null) {
return existing;
} else {
final T newInstance = factory.get();
if (reference.compareAndSet(null, newInstance)) {
return newInstance;
} else {
return reference.get();
try {
return reference.updateAndGet(invokeFactoryIfNull(factory));
} catch (InitializationException e) {
if (exceptionType.isInstance(e.getCause())) {
throw exceptionType.cast(e.getCause());
} else {
throw e;
}
}
}
}
private static <T, E extends Exception> UnaryOperator<T> invokeFactoryIfNull(SupplierThrowingException<T, E> factory) throws InitializationException {
return currentValue -> {
if (currentValue == null) {
try {
return factory.get();
} catch (RuntimeException e) {
throw e; // don't catch unchecked exceptions
} catch (Exception e) {
throw new InitializationException(e);
}
} else {
return currentValue;
}
};
}
private static class InitializationException extends RuntimeException {
public InitializationException(Throwable cause) {
super(cause);
}
}
}

View File

@@ -1,7 +1,7 @@
package org.cryptomator.common;
@FunctionalInterface
public interface RunnableThrowingException<T extends Exception> {
public interface RunnableThrowingException<T extends Throwable> {
void run() throws T;

View File

@@ -0,0 +1,81 @@
/*******************************************************************************
* 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.common;
import java.util.Comparator;
import org.apache.commons.lang3.StringUtils;
/**
* Compares version strings according to <a href="http://semver.org/spec/v2.0.0.html">SemVer 2.0.0</a>.
*/
public class SemVerComparator implements Comparator<String> {
private static final char VERSION_SEP = '.'; // http://semver.org/spec/v2.0.0.html#spec-item-2
private static final String PRE_RELEASE_SEP = "-"; // http://semver.org/spec/v2.0.0.html#spec-item-9
private static final String BUILD_SEP = "+"; // http://semver.org/spec/v2.0.0.html#spec-item-10
@Override
public int compare(String version1, String version2) {
// "Build metadata SHOULD be ignored when determining version precedence.
// Thus two versions that differ only in the build metadata, have the same precedence."
String v1WithoutBuildMetadata = StringUtils.substringBefore(version1, BUILD_SEP);
String v2WithoutBuildMetadata = StringUtils.substringBefore(version2, BUILD_SEP);
if (v1WithoutBuildMetadata.equals(v2WithoutBuildMetadata)) {
return 0;
}
String v1MajorMinorPatch = StringUtils.substringBefore(v1WithoutBuildMetadata, PRE_RELEASE_SEP);
String v2MajorMinorPatch = StringUtils.substringBefore(v2WithoutBuildMetadata, PRE_RELEASE_SEP);
String v1PreReleaseVersion = StringUtils.substringAfter(v1WithoutBuildMetadata, PRE_RELEASE_SEP);
String v2PreReleaseVersion = StringUtils.substringAfter(v2WithoutBuildMetadata, PRE_RELEASE_SEP);
return compare(v1MajorMinorPatch, v1PreReleaseVersion, v2MajorMinorPatch, v2PreReleaseVersion);
}
private int compare(String v1MajorMinorPatch, String v1PreReleaseVersion, String v2MajorMinorPatch, String v2PreReleaseVersion) {
int comparisonResult = compareNumericallyThenLexicographically(v1MajorMinorPatch, v2MajorMinorPatch);
if (comparisonResult == 0) {
if (v1PreReleaseVersion.isEmpty()) {
return 1; // 1.0.0 > 1.0.0-BETA
} else if (v2PreReleaseVersion.isEmpty()) {
return -1; // 1.0.0-BETA < 1.0.0
} else {
return compareNumericallyThenLexicographically(v1PreReleaseVersion, v2PreReleaseVersion);
}
} else {
return comparisonResult;
}
}
private int compareNumericallyThenLexicographically(String version1, String version2) {
final String[] vComps1 = StringUtils.split(version1, VERSION_SEP);
final String[] vComps2 = StringUtils.split(version2, VERSION_SEP);
final int commonCompCount = Math.min(vComps1.length, vComps2.length);
for (int i = 0; i < commonCompCount; i++) {
int subversionComparisionResult = 0;
try {
final int v1 = Integer.parseInt(vComps1[i]);
final int v2 = Integer.parseInt(vComps2[i]);
subversionComparisionResult = v1 - v2;
} catch (NumberFormatException ex) {
// ok, lets compare this fragment lexicographically
subversionComparisionResult = vComps1[i].compareTo(vComps2[i]);
}
if (subversionComparisionResult != 0) {
return subversionComparisionResult;
}
}
// all in common so far? longest version string is considered the higher version:
return vComps1.length - vComps2.length;
}
}

View File

@@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (c) 2016 Markus Kreusch and others.
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Markus Kreusch - initial implementation
*******************************************************************************/
package org.cryptomator.common;
import java.util.stream.Stream;
/**
* Utility to print stack traces while analyzing issues.
*
* @author Markus Kreusch
*/
public class StackTrace {
public static void print(String message) {
Thread thread = Thread.currentThread();
System.err.println(stackTraceFor(message, thread));
}
private static String stackTraceFor(String message, Thread thread) {
StringBuilder result = new StringBuilder();
appendMessageAndThreadName(result, message, thread);
appendStackTrace(thread, result);
return result.toString();
}
private static void appendStackTrace(Thread thread, StringBuilder result) {
Stream.of(thread.getStackTrace()) //
.skip(4) //
.forEach(stackTraceElement -> append(stackTraceElement, result));
}
private static void appendMessageAndThreadName(StringBuilder result, String message, Thread thread) {
result //
.append('[') //
.append(thread.getName()) //
.append("] ") //
.append(message);
}
private static void append(StackTraceElement stackTraceElement, StringBuilder result) {
String className = stackTraceElement.getClassName();
String methodName = stackTraceElement.getMethodName();
String fileName = stackTraceElement.getFileName();
int lineNumber = stackTraceElement.getLineNumber();
result.append('\n') //
.append(className).append(':').append(methodName) //
.append(" (").append(fileName).append(':').append(lineNumber).append(')');
}
}

View File

@@ -0,0 +1,8 @@
package org.cryptomator.common;
@FunctionalInterface
public interface SupplierThrowingException<T, E extends Throwable> {
T get() throws E;
}

View File

@@ -0,0 +1,98 @@
/*******************************************************************************
* Copyright (c) 2014, 2016 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
******************************************************************************/
package org.cryptomator.common.settings;
import java.util.function.Consumer;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
public class Settings {
public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65535;
public static final boolean DEFAULT_CHECK_FOR_UDPATES = true;
public static final int DEFAULT_PORT = 42427;
public static final boolean DEFAULT_USE_IPV6 = false;
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
public static final String DEFAULT_GVFS_SCHEME = "dav";
public static final boolean DEFAULT_DEBUG_MODE = false;
private final Consumer<Settings> saveCmd;
private final ObservableList<VaultSettings> directories = FXCollections.observableArrayList();
private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UDPATES);
private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
private final BooleanProperty useIpv6 = new SimpleBooleanProperty(DEFAULT_USE_IPV6);
private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
private final StringProperty preferredGvfsScheme = new SimpleStringProperty(DEFAULT_GVFS_SCHEME);
private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE);
/**
* Package-private constructor; use {@link SettingsProvider}.
*/
Settings(Consumer<Settings> saveCmd) {
this.saveCmd = saveCmd;
directories.addListener((ListChangeListener.Change<? extends VaultSettings> change) -> this.save());
checkForUpdates.addListener(this::somethingChanged);
port.addListener(this::somethingChanged);
useIpv6.addListener(this::somethingChanged);
numTrayNotifications.addListener(this::somethingChanged);
preferredGvfsScheme.addListener(this::somethingChanged);
debugMode.addListener(this::somethingChanged);
}
private void somethingChanged(ObservableValue<?> observable, Object oldValue, Object newValue) {
this.save();
}
void save() {
if (saveCmd != null) {
saveCmd.accept(this);
}
}
/* Getter/Setter */
public ObservableList<VaultSettings> getDirectories() {
return directories;
}
public BooleanProperty checkForUpdates() {
return checkForUpdates;
}
public IntegerProperty port() {
return port;
}
public BooleanProperty useIpv6() {
return useIpv6;
}
public IntegerProperty numTrayNotifications() {
return numTrayNotifications;
}
public StringProperty preferredGvfsScheme() {
return preferredGvfsScheme;
}
public BooleanProperty debugMode() {
return debugMode;
}
}

View File

@@ -0,0 +1,103 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.common.settings;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
public class SettingsJsonAdapter extends TypeAdapter<Settings> {
private static final Logger LOG = LoggerFactory.getLogger(SettingsJsonAdapter.class);
private final Consumer<Settings> saveCmd;
private final VaultSettingsJsonAdapter vaultSettingsJsonAdapter = new VaultSettingsJsonAdapter();
public SettingsJsonAdapter(Consumer<Settings> saveCmd) {
this.saveCmd = saveCmd;
}
@Override
public void write(JsonWriter out, Settings value) throws IOException {
out.beginObject();
out.name("directories");
writeVaultSettingsArray(out, value.getDirectories());
out.name("checkForUpdatesEnabled").value(value.checkForUpdates().get());
out.name("port").value(value.port().get());
out.name("useIpv6").value(value.useIpv6().get());
out.name("numTrayNotifications").value(value.numTrayNotifications().get());
out.name("preferredGvfsScheme").value(value.preferredGvfsScheme().get());
out.name("debugMode").value(value.debugMode().get());
out.endObject();
}
private void writeVaultSettingsArray(JsonWriter out, Iterable<VaultSettings> vaultSettings) throws IOException {
out.beginArray();
for (VaultSettings value : vaultSettings) {
vaultSettingsJsonAdapter.write(out, value);
}
out.endArray();
}
@Override
public Settings read(JsonReader in) throws IOException {
Settings settings = new Settings(saveCmd);
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
switch (name) {
case "directories":
settings.getDirectories().addAll(readVaultSettingsArray(in, settings));
break;
case "checkForUpdatesEnabled":
settings.checkForUpdates().set(in.nextBoolean());
break;
case "port":
settings.port().set(in.nextInt());
break;
case "useIpv6":
settings.useIpv6().set(in.nextBoolean());
break;
case "numTrayNotifications":
settings.numTrayNotifications().set(in.nextInt());
break;
case "preferredGvfsScheme":
settings.preferredGvfsScheme().set(in.nextString());
break;
case "debugMode":
settings.debugMode().set(in.nextBoolean());
break;
default:
LOG.warn("Unsupported vault setting found in JSON: " + name);
in.skipValue();
}
}
in.endObject();
return settings;
}
private List<VaultSettings> readVaultSettingsArray(JsonReader in, Settings settings) throws IOException {
List<VaultSettings> result = new ArrayList<>();
in.beginArray();
while (!JsonToken.END_ARRAY.equals(in.peek())) {
result.add(vaultSettingsJsonAdapter.read(in, settings));
}
in.endArray();
return result;
}
}

View File

@@ -0,0 +1,136 @@
/*******************************************************************************
* 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.common.settings;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Optional;
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.Provider;
import javax.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.LazyInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@Singleton
public class SettingsProvider implements Provider<Settings> {
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
private static final Path DEFAULT_SETTINGS_PATH;
private static final long SAVE_DELAY_MS = 1000;
static {
final FileSystem fs = FileSystems.getDefault();
if (SystemUtils.IS_OS_WINDOWS) {
DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, "AppData/Roaming/Cryptomator/settings.json");
} else if (SystemUtils.IS_OS_MAC_OSX) {
DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, "Library/Application Support/Cryptomator/settings.json");
} else {
DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator/settings.json");
}
}
private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor();
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
private final AtomicReference<Settings> settings = new AtomicReference<>();
private final SettingsJsonAdapter settingsJsonAdapter = new SettingsJsonAdapter(this::scheduleSave);
private final Gson gson;
@Inject
public SettingsProvider() {
this.gson = new GsonBuilder() //
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
.create();
}
private Path getSettingsPath() {
final String settingsPathProperty = System.getProperty("cryptomator.settingsPath");
return Optional.ofNullable(settingsPathProperty).filter(StringUtils::isNotBlank).map(this::replaceHomeDir).map(FileSystems.getDefault()::getPath).orElse(DEFAULT_SETTINGS_PATH);
}
private String replaceHomeDir(String path) {
if (path.startsWith("~/")) {
return SystemUtils.USER_HOME + path.substring(1);
} else {
return path;
}
}
@Override
public Settings get() {
return LazyInitializer.initializeLazily(settings, this::load);
}
private Settings load() {
Settings settings;
final Path settingsPath = getSettingsPath();
try (InputStream in = Files.newInputStream(settingsPath, StandardOpenOption.READ); //
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
settings = gson.fromJson(reader, Settings.class);
LOG.info("Settings loaded from " + settingsPath);
} catch (IOException e) {
LOG.info("Failed to load settings, creating new one.");
settings = new Settings(this::scheduleSave);
}
return 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) {
assert settings != null : "method should only be invoked by #scheduleSave, which checks for null";
final Path settingsPath = getSettingsPath();
try {
Files.createDirectories(settingsPath.getParent());
try (OutputStream out = Files.newOutputStream(settingsPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); //
Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
gson.toJson(settings, writer);
LOG.info("Settings saved to " + settingsPath);
}
} catch (IOException e) {
LOG.error("Failed to save settings.", e);
}
}
}

View File

@@ -0,0 +1,140 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.common.settings;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Base64;
import java.util.Objects;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.fxmisc.easybind.EasyBind;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
public class VaultSettings {
private final Settings settings;
private final String id;
private final ObjectProperty<Path> path = new SimpleObjectProperty<>();
private final StringProperty mountName = new SimpleStringProperty();
private final StringProperty winDriveLetter = new SimpleStringProperty();
private final BooleanProperty mountAfterUnlock = new SimpleBooleanProperty();
private final BooleanProperty revealAfterMount = new SimpleBooleanProperty();
public VaultSettings(Settings settings, String id) {
this.settings = settings;
this.id = Objects.requireNonNull(id);
EasyBind.subscribe(path, this::deriveMountNameFromPath);
path.addListener(this::somethingChanged);
mountName.addListener(this::somethingChanged);
winDriveLetter.addListener(this::somethingChanged);
mountAfterUnlock.addListener(this::somethingChanged);
}
private void somethingChanged(ObservableValue<?> observable, Object oldValue, Object newValue) {
settings.save();
}
private void deriveMountNameFromPath(Path path) {
if (path != null && StringUtils.isBlank(mountName.get())) {
mountName.set(normalizeMountName(path.getFileName().toString()));
}
}
public static VaultSettings withRandomId(Settings settings) {
return new VaultSettings(settings, generateId());
}
private static String generateId() {
return asBase64String(nineBytesFrom(UUID.randomUUID()));
}
private static String asBase64String(byte[] bytes) {
byte[] base64Bytes = Base64.getUrlEncoder().encode(bytes);
return new String(base64Bytes, StandardCharsets.US_ASCII);
}
private static byte[] nineBytesFrom(UUID uuid) {
ByteBuffer uuidBuffer = ByteBuffer.allocate(9);
uuidBuffer.putLong(uuid.getMostSignificantBits());
uuidBuffer.put((byte) (uuid.getLeastSignificantBits() & 0xFF));
uuidBuffer.flip();
return uuidBuffer.array();
}
public static String normalizeMountName(String mountName) {
String normalizedMountName = StringUtils.stripAccents(mountName);
StringBuilder builder = new StringBuilder();
for (char c : normalizedMountName.toCharArray()) {
if (Character.isWhitespace(c)) {
if (builder.length() == 0 || builder.charAt(builder.length() - 1) != '_') {
builder.append('_');
}
} else if (c < 127 && Character.isLetterOrDigit(c)) {
builder.append(c);
} else {
if (builder.length() == 0 || builder.charAt(builder.length() - 1) != '_') {
builder.append('_');
}
}
}
return builder.toString();
}
/* Getter/Setter */
public String getId() {
return id;
}
public ObjectProperty<Path> path() {
return path;
}
public StringProperty mountName() {
return mountName;
}
public StringProperty winDriveLetter() {
return winDriveLetter;
}
public BooleanProperty mountAfterUnlock() {
return mountAfterUnlock;
}
public BooleanProperty revealAfterMount() {
return revealAfterMount;
}
/* Hashcode/Equals */
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof VaultSettings && obj.getClass().equals(this.getClass())) {
VaultSettings other = (VaultSettings) obj;
return Objects.equals(this.id, other.id);
} else {
return false;
}
}
}

View File

@@ -0,0 +1,78 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.common.settings;
import java.io.IOException;
import java.nio.file.Paths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
class VaultSettingsJsonAdapter {
private static final Logger LOG = LoggerFactory.getLogger(VaultSettingsJsonAdapter.class);
public void write(JsonWriter out, VaultSettings value) throws IOException {
out.beginObject();
out.name("id").value(value.getId());
out.name("path").value(value.path().get().toString());
out.name("mountName").value(value.mountName().get());
out.name("winDriveLetter").value(value.winDriveLetter().get());
out.name("mountAfterUnlock").value(value.mountAfterUnlock().get());
out.name("revealAfterMount").value(value.revealAfterMount().get());
out.endObject();
}
public VaultSettings read(JsonReader in, Settings settings) throws IOException {
String id = null;
String path = null;
String mountName = null;
String winDriveLetter = null;
boolean mountAfterUnlock = true;
boolean revealAfterMount = true;
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
switch (name) {
case "id":
id = in.nextString();
break;
case "path":
path = in.nextString();
break;
case "mountName":
mountName = in.nextString();
break;
case "winDriveLetter":
winDriveLetter = in.nextString();
break;
case "mountAfterUnlock":
mountAfterUnlock = in.nextBoolean();
break;
case "revealAfterMount":
revealAfterMount = in.nextBoolean();
break;
default:
LOG.warn("Unsupported vault setting found in JSON: " + name);
in.skipValue();
}
}
in.endObject();
VaultSettings vaultSettings = (id == null) ? VaultSettings.withRandomId(settings) : new VaultSettings(settings, id);
vaultSettings.mountName().set(mountName);
vaultSettings.path().set(Paths.get(path));
vaultSettings.winDriveLetter().set(winDriveLetter);
vaultSettings.mountAfterUnlock().set(mountAfterUnlock);
vaultSettings.revealAfterMount().set(revealAfterMount);
return vaultSettings;
}
}

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;
@@ -21,8 +21,10 @@ public class SemVerComparatorTest {
@Test
public void compareEqualVersions() {
final int comparisonResult = semVerComparator.compare("1.23.4", "1.23.4");
Assert.assertEquals(0, Integer.signum(comparisonResult));
Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4")));
Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-alpha")));
Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4+20170101", "1.23.4+20171231")));
Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4-alpha+20170101", "1.23.4-alpha+20171231")));
}
// newer versions in first argument
@@ -32,7 +34,11 @@ public class SemVerComparatorTest {
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.5", "1.23.4")));
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.24.4", "1.23.4")));
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23")));
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4a", "1.23.4")));
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4-SNAPSHOT")));
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4-56.78")));
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-beta", "1.23.4-alpha")));
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-alpha.1", "1.23.4-alpha")));
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-56.79", "1.23.4-56.78")));
}
// newer versions in second argument
@@ -42,7 +48,11 @@ public class SemVerComparatorTest {
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.5")));
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.24.4")));
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23", "1.23.4")));
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4a")));
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-SNAPSHOT", "1.23.4")));
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-56.78", "1.23.4")));
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-beta")));
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-alpha.1")));
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-56.78", "1.23.4-56.79")));
}
}

View File

@@ -9,8 +9,8 @@ import static org.mockito.Mockito.when;
import java.util.function.Function;
import org.cryptomator.common.WeakValuedCache;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
@@ -83,6 +83,7 @@ public class WeakValuedCacheTest {
assertThat(result, is(sameInstance(theValue)));
}
@Ignore
@Test
public void testCacheDoesNotPreventGarbageCollectionOfValues() {
when(loader.apply(A_KEY)).thenAnswer(this::createValueUsingMoreThanHalfTheJvmMemory);

View File

@@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.common.settings;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
public class SettingsJsonAdapterTest {
private final SettingsJsonAdapter adapter = new SettingsJsonAdapter(this::noop);
private void noop(Settings settings) {
}
@Test
public void testDeserialize() throws IOException {
String vault1Json = "{\"id\": \"1\", \"path\": \"/vault1\", \"mountName\": \"vault1\", \"winDriveLetter\": \"X\"}";
String vault2Json = "{\"id\": \"2\", \"path\": \"/vault2\", \"mountName\": \"vault2\", \"winDriveLetter\": \"Y\"}";
String json = "{\"directories\": [" + vault1Json + "," + vault2Json + "]," //
+ "\"checkForUpdatesEnabled\": true,"//
+ "\"port\": 8080,"//
+ "\"useIpv6\": true,"//
+ "\"numTrayNotifications\": 42}";
Settings settings = adapter.fromJson(json);
Assert.assertTrue(settings.checkForUpdates().get());
Assert.assertEquals(2, settings.getDirectories().size());
Assert.assertEquals(8080, settings.port().get());
Assert.assertTrue(settings.useIpv6().get());
Assert.assertEquals(42, settings.numTrayNotifications().get());
Assert.assertEquals("dav", settings.preferredGvfsScheme().get());
}
}

View File

@@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.common.settings;
import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Paths;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import com.google.gson.stream.JsonReader;
public class VaultSettingsJsonAdapterTest {
private final VaultSettingsJsonAdapter adapter = new VaultSettingsJsonAdapter();
@Test
public void testDeserialize() throws IOException {
String json = "{\"id\": \"foo\", \"path\": \"/foo/bar\", \"mountName\": \"test\", \"winDriveLetter\": \"X\", \"shouldBeIgnored\": true}";
JsonReader jsonReader = new JsonReader(new StringReader(json));
Settings settings = Mockito.mock(Settings.class);
VaultSettings vaultSettings = adapter.read(jsonReader, settings);
Assert.assertEquals("foo", vaultSettings.getId());
Assert.assertEquals(Paths.get("/foo/bar"), vaultSettings.path().get());
Assert.assertEquals("test", vaultSettings.mountName().get());
Assert.assertEquals("X", vaultSettings.winDriveLetter().get());
}
}

View File

@@ -6,21 +6,21 @@
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.ui.model;
package org.cryptomator.common.settings;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class VaultTest {
public class VaultSettingsTest {
@Test
public void testNormalize() throws Exception {
assertEquals("_", Vault.normalize(" "));
assertEquals("a", Vault.normalize("ä"));
assertEquals("C", Vault.normalize("Ĉ"));
assertEquals("_", Vault.normalize(":"));
assertEquals("", Vault.normalize("汉语"));
assertEquals("_", VaultSettings.normalizeMountName(" "));
assertEquals("a", VaultSettings.normalizeMountName("ä"));
assertEquals("C", VaultSettings.normalizeMountName("Ĉ"));
assertEquals("_", VaultSettings.normalizeMountName(":"));
assertEquals("_", VaultSettings.normalizeMountName("汉语"));
}
}

View File

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

View File

@@ -1,55 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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;
import java.io.IOException;
import java.io.UncheckedIOException;
import com.google.common.io.ByteStreams;
class Copier {
public static void copy(Folder source, Folder destination) {
assertFoldersAreNotNested(source, destination);
destination.delete();
destination.create();
source.files().forEach(sourceFile -> {
File destinationFile = destination.file(sourceFile.name());
sourceFile.copyTo(destinationFile);
});
source.folders().forEach(sourceFolder -> {
Folder destinationFolder = destination.folder(sourceFolder.name());
sourceFolder.copyTo(destinationFolder);
});
}
public static void copy(File source, File destination) {
try (OpenFiles openFiles = DeadlockSafeFileOpener.withReadable(source).andWritable(destination).open()) {
ReadableFile readable = openFiles.readable(source);
WritableFile writable = openFiles.writable(destination);
writable.truncate();
ByteStreams.copy(readable, writable);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private static void assertFoldersAreNotNested(Folder source, Folder destination) {
if (source.isAncestorOf(destination)) {
throw new IllegalArgumentException("Can not copy parent to child directory (src: " + source + ", dst: " + destination + ")");
}
if (destination.isAncestorOf(source)) {
throw new IllegalArgumentException("Can not copy child to parent directory (src: " + source + ", dst: " + destination + ")");
}
}
}

View File

@@ -1,69 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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;
import static java.lang.String.format;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.function.Consumer;
public class DeadlockSafeFileOpener {
public static DeadlockSafeFileOpener withReadable(File file) {
return new DeadlockSafeFileOpener().andReadable(file);
}
public static DeadlockSafeFileOpener withWritable(File file) {
return new DeadlockSafeFileOpener().andWritable(file);
}
private final SortedMap<File, Consumer<File>> filesWithOperation = new TreeMap<>();
private final Map<File, ReadableFile> readableFiles = new HashMap<>();
private final Map<File, WritableFile> writableFiles = new HashMap<>();
private DeadlockSafeFileOpener() {
}
public DeadlockSafeFileOpener andReadable(File file) {
if (filesWithOperation.put(file, this::openReadable) != null) {
throw new IllegalArgumentException(format("File %s already marked for opening", file));
}
return this;
}
public DeadlockSafeFileOpener andWritable(File file) {
if (filesWithOperation.put(file, this::openWritable) != null) {
throw new IllegalArgumentException(format("File %s already marked for opening", file));
}
return this;
}
private void openReadable(File file) {
readableFiles.put(file, file.openReadable());
}
private void openWritable(File file) {
writableFiles.put(file, file.openWritable());
}
public OpenFiles open() {
try {
filesWithOperation.forEach((file, openAction) -> openAction.accept(file));
} catch (RuntimeException e) {
OpenFiles.cleanup(readableFiles.values(), writableFiles.values());
throw e;
}
return new OpenFiles(readableFiles, writableFiles);
}
}

View File

@@ -1,15 +0,0 @@
package org.cryptomator.filesystem;
public class Deleter {
/**
* Deletes all and only the content of a given {@link Folder} but <b>not</b> the folder itself.
*/
public static void deleteContent(Folder folder) {
if (folder.exists()) {
folder.folders().forEach(Folder::delete);
folder.files().forEach(File::delete);
}
}
}

View File

@@ -1,81 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Markus Kreusch
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
******************************************************************************/
package org.cryptomator.filesystem;
import java.io.IOException;
import java.io.UncheckedIOException;
/**
* A {@link File} in a {@link FileSystem}.
*
* @author Markus Kreusch
*/
public interface File extends Node, Comparable<File> {
static final int EOF = -1;
/**
* <p>
* Opens this file for reading.
* <p>
* An implementation guarantees, that per {@link FileSystem} and
* {@code File} while a {@code ReadableFile} is open no {@link WritableFile}
* can be open and vice versa. A {@link ReadableFile} is open when returned
* from this method and not yet closed using {@link ReadableFile#close()}.
* <br>
* A limitation to the number of {@code ReadableFiles} is in general not
* required but may be set by a specific implementation.
* <p>
* If a {@link WritableFile} for this {@code File} is open the invocation of
* this method will block regarding the specified timeout.<br>
* In addition implementations may block to lock the required IO resources
* to read the file.
*
* @return a {@link ReadableFile} to work with
* @throws UncheckedIOException
* if an {@link IOException} occurs while opening the file, the
* file does not exist or is a directory
*/
ReadableFile openReadable() throws UncheckedIOException;
/**
* <p>
* Opens this file for writing.
* <p>
* If the file does not exist a new empty file is created.
* <p>
* An implementation guarantees, that per {@link FileSystem} and
* {@code File} only one {@link WritableFile} is open at a time. A
* {@code WritableFile} is open when returned from this method and not yet
* closed using {@link WritableFile#close()} or
* {@link WritableFile#delete()}.<br>
* In addition while a {@code WritableFile} is open no {@link ReadableFile}
* can be open and vice versa.
* <p>
* If a {@code Readable-} or {@code WritableFile} for this {@code File} is
* open the invocation of this method will block regarding the specified
* timeout.<br>
* In addition implementations may block to lock the required IO resources
* to read the file.
*
* @return a {@link WritableFile} to work with
* @throws UncheckedIOException
* if an {@link IOException} occurs while opening the file or
* the file is a directory
*/
WritableFile openWritable() throws UncheckedIOException;
default void copyTo(File destination) {
Copier.copy(this, destination);
}
/**
* Moves this file including content to a new location specified by <code>destination</code>.
*/
void moveTo(File destination) throws UncheckedIOException;
}

View File

@@ -1,30 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Markus Kreusch
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
******************************************************************************/
package org.cryptomator.filesystem;
import java.util.Optional;
/**
* The root folder of a file system.
*
* @author Markus Kreusch
*/
public interface FileSystem extends Folder {
/**
* @return an empty {@link Optional} because a {@link FileSystem} represents
* the root {@link Folder} and thus does not have a parent
*/
@Override
default Optional<? extends Folder> parent() {
return Optional.empty();
}
Optional<Long> quotaUsedBytes();
Optional<Long> quotaAvailableBytes();
}

View File

@@ -1,145 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Markus Kreusch
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
******************************************************************************/
package org.cryptomator.filesystem;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.stream.Stream;
/**
* A {@link Folder} in a {@link FileSystem}.
*
* @author Markus Kreusch
*/
public interface Folder extends Node {
/**
* <p>
* Creates a {@link Stream} over all child nodes of this {@code Folder}.
* <p>
* <b>Note:</b> The {@link Stream} may be lazily populated and thus
* {@link IOException IOExceptions} may occurs after this method returned.
* In this case implementors should throw a {@link UncheckedIOException}
* from any method that produces an {@link IOException}. Thus users should
* expect {@link UncheckedIOException UncheckedIOExceptions} when invoking
* methods on the returned {@code Stream}.
*
* @return the created {@code Stream}
* @throws UncheckedIOException
* if an {@link IOException} occurs while initializing the
* stream or the {@code Folder} does not exist
*/
Stream<? extends Node> children() throws UncheckedIOException;
/**
* <p>
* Returns the child {@link Node} in this directory of type {@link File}
* with the specified name.
* <p>
* This operation always returns a {@link File} without checking if the file
* exists or is a {@link Folder} instead.
*/
File file(String name) throws UncheckedIOException;
/**
* Returns a file by resolving a path relative to this folder.
*
* @param path A unix-style path, which is always relative to this folder, no matter if it starts with a slash or not. Path must not be empty.
* @return File with the given path relative to this folder
* @throws IllegalArgumentException
* if relativePath is empty
*/
default File resolveFile(String relativePath) throws UncheckedIOException, IllegalArgumentException {
return PathResolver.resolveFile(this, relativePath);
}
/**
* <p>
* Returns the child {@link Node} in this directory of type {@link Folder}
* with the specified name.
* <p>
* This operation always returns a {@link Folder} without checking if the
* folder exists or is a {@link File} instead.
*/
Folder folder(String name) throws UncheckedIOException;
/**
* Returns a folder by resolving a path relative to this folder.
*
* @param path A unix-style path, which is always relative to this folder, no matter if it starts with a slash or not. Path may be empty.
* @return Folder with the given path relative to this folder. Returns <code>this</code> if path is empty.
*/
default Folder resolveFolder(String relativePath) throws UncheckedIOException {
return PathResolver.resolveFolder(this, relativePath);
}
/**
* Creates the directory including all parent directories, if it doesn't
* exist yet. No effect, if folder already exists.
*
* @throws UncheckedIOException
* if an {@link IOException} occurs while creating the folder or
* one of its parents
*/
void create() throws UncheckedIOException;
/**
* Recusively copies this directory and all its contents to (not into) the
* given destination, creating nonexisting parent directories. If the target
* exists it is deleted before performing the copy.
*
* @param target
* Destination folder. Must not be a descendant of this folder.
*/
default void copyTo(Folder target) throws UncheckedIOException {
Copier.copy(this, target);
}
/**
* Moves this directory and its contents to the given destination. If the
* target exists it is deleted before performing the move.
*/
void moveTo(Folder target);
/**
* @return the result of {@link #children()} filtered to contain only
* {@link File Files}
*/
default Stream<? extends File> files() throws UncheckedIOException {
return children() //
.filter(File.class::isInstance) //
.map(File.class::cast);
}
/**
* @return the result of {@link #children()} filtered to contain only
* {@link Folder Folders}
*/
default Stream<? extends Folder> folders() throws UncheckedIOException {
return children() //
.filter(Folder.class::isInstance) //
.map(Folder.class::cast);
}
/**
* Recursively checks whether this folder or any subfolder contains the
* given node.
*
* @param node
* Potential child, grandchild, ...
* @return <code>true</code> if this folder is an ancestor of the node.
*/
default boolean isAncestorOf(Node node) {
if (!node.parent().isPresent()) {
return false;
} else if (node.parent().get().equals(this)) {
return true;
} else {
return this.isAncestorOf(node.parent().get());
}
}
}

View File

@@ -1,131 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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;
import static java.lang.String.format;
import java.util.function.Consumer;
public class FolderVisitor {
private final Consumer<Folder> beforeFolderVisitor;
private final Consumer<Folder> afterFolderVisitor;
private final Consumer<File> fileVisitor;
private final Consumer<Node> nodeVisitor;
private final int maxDepth;
public FolderVisitor(FolderVisitorBuilder builder) {
this.beforeFolderVisitor = builder.beforeFolderVisitor;
this.afterFolderVisitor = builder.afterFolderVisitor;
this.fileVisitor = builder.fileVisitor;
this.nodeVisitor = builder.nodeVisitor;
this.maxDepth = builder.maxDepth;
}
public static FolderVisitorBuilder folderVisitor() {
return new FolderVisitorBuilder();
}
public FolderVisitor visit(Folder folder) {
return visit(folder, 0);
}
public FolderVisitor visit(File file) {
return visit(file, 0);
}
private FolderVisitor visit(Folder folder, int depth) {
beforeFolderVisitor.accept(folder);
nodeVisitor.accept(folder);
final int childDepth = depth + 1;
if (childDepth <= maxDepth) {
folder.folders().forEach(childFolder -> visit(childFolder, childDepth));
folder.files().forEach(childFile -> visit(childFile, childDepth));
}
afterFolderVisitor.accept(folder);
return this;
}
private FolderVisitor visit(File file, int depth) {
nodeVisitor.accept(file);
fileVisitor.accept(file);
return this;
}
public static class FolderVisitorBuilder {
private Consumer<Folder> beforeFolderVisitor = noOp();
private Consumer<Folder> afterFolderVisitor = noOp();
private Consumer<File> fileVisitor = noOp();
private Consumer<Node> nodeVisitor = noOp();
private int maxDepth = Integer.MAX_VALUE;
private FolderVisitorBuilder() {
}
public FolderVisitorBuilder beforeFolder(Consumer<Folder> beforeFolderVisitor) {
if (beforeFolderVisitor == null) {
throw new IllegalArgumentException("Vistior may not be null");
}
this.beforeFolderVisitor = beforeFolderVisitor;
return this;
}
public FolderVisitorBuilder afterFolder(Consumer<Folder> afterFolderVisitor) {
if (afterFolderVisitor == null) {
throw new IllegalArgumentException("Vistior may not be null");
}
this.afterFolderVisitor = afterFolderVisitor;
return this;
}
public FolderVisitorBuilder forEachFile(Consumer<File> fileVisitor) {
if (fileVisitor == null) {
throw new IllegalArgumentException("Vistior may not be null");
}
this.fileVisitor = fileVisitor;
return this;
}
public FolderVisitorBuilder forEachNode(Consumer<Node> nodeVisitor) {
if (nodeVisitor == null) {
throw new IllegalArgumentException("Vistior may not be null");
}
this.nodeVisitor = nodeVisitor;
return this;
}
public FolderVisitorBuilder withMaxDepth(int maxDepth) {
if (maxDepth < 0) {
throw new IllegalArgumentException(format("maxDepth must not be smaller 0 but was %d", maxDepth));
}
this.maxDepth = maxDepth;
return this;
}
public FolderVisitor visit(Folder folder) {
return build().visit(folder);
}
public FolderVisitor visit(File file) {
return build().visit(file);
}
public FolderVisitor build() {
return new FolderVisitor(this);
}
private static <T> Consumer<T> noOp() {
return ignoredParameter -> {
};
}
}
}

View File

@@ -1,100 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Markus Kreusch
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
******************************************************************************/
package org.cryptomator.filesystem;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.time.Instant;
import java.util.Optional;
/**
* Represents a node, namely a {@link File} or {@link Folder}, in a
* {@link FileSystem}.
* <p>
* A node's identity (i.e. {@link #hashCode()} and {@link #equals(Object)}) depends on its parent node and its name (forming the node's path).
* These properties are meant to be immutable. This means that e.g. moving a node doesn't modify the node's identity but rather transfers properties to the destination node.
*
* @author Markus Kreusch
* @see Folder
* @see File
*/
public interface Node {
String name() throws UncheckedIOException;
/**
* @return Optional parent folder. No parent is present for the root node (see {@link FileSystem#parent()}).
*/
Optional<? extends Folder> parent() throws UncheckedIOException;
/**
* @return <code>true</code> if the node exists.
*/
boolean exists() throws UncheckedIOException;
/**
* <p>
* Deletes the node if it exists.
* <p>
* Does nothing if the node does not exist.
*/
void delete() throws UncheckedIOException;
/**
* <p>
* Determines the last modified date of this node.
*
* @returns the last modified date of the file
*/
Instant lastModified() throws UncheckedIOException;
/**
* <p>
* Sets the last modified date of the file.
*
* @param lastModified the time to set as creation time
*/
void setLastModified(Instant lastModified) throws UncheckedIOException;
/**
* <p>
* Determines the creation time of this node.
* <p>
* Note: Getting the creation time may not be supported by all {@link FileSystem FileSystems}.
*
* @returns the creation time of the file or {@link Optional#empty()} if not supported
*/
default Optional<Instant> creationTime() throws UncheckedIOException {
return Optional.empty();
}
/**
* <p>
* Sets the creation time of this node.
* <p>
* Setting the creation time may not be supported by all {@link FileSystem FileSystems}. If the {@code FileSystem} this {@code Node} belongs to does not support the
* setting the creation time the behavior of this method is unspecified.
*
* @param creationTime the time to set as creation time
*/
default void setCreationTime(Instant creationTime) throws UncheckedIOException {
throw new UncheckedIOException(new IOException("CreationTime not supported"));
}
/**
* @return the {@link FileSystem} this Node belongs to
*/
default FileSystem fileSystem() {
return parent() //
.map(Node::fileSystem) //
.orElseGet(() -> (FileSystem) this);
}
default boolean belongsToSameFilesystem(Node other) {
return fileSystem() == other.fileSystem();
}
}

View File

@@ -1,71 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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;
import java.io.UncheckedIOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OpenFiles implements AutoCloseable {
private final static Logger LOG = LoggerFactory.getLogger(OpenFiles.class);
private final Map<File, ReadableFile> readableFiles;
private final Map<File, WritableFile> writableFiles;
public OpenFiles(Map<File, ReadableFile> readableFiles, Map<File, WritableFile> writableFiles) {
this.readableFiles = readableFiles;
this.writableFiles = writableFiles;
}
@Override
public void close() throws UncheckedIOException {
OpenFiles.cleanup(readableFiles.values(), writableFiles.values());
}
public ReadableFile readable(File file) {
return readableFiles.computeIfAbsent(file, fileNotOpenForReading -> {
throw new IllegalArgumentException(String.format("File %s is not open for reading", fileNotOpenForReading));
});
}
public WritableFile writable(File file) {
return writableFiles.computeIfAbsent(file, fileNotOpenForWriting -> {
throw new IllegalArgumentException(String.format("File %s is not open for writing", fileNotOpenForWriting));
});
}
static void cleanup(Collection<ReadableFile> readableFiles, Collection<WritableFile> writableFiles) {
Iterator<? extends AutoCloseable> iterator = Stream.concat(readableFiles.stream(), writableFiles.stream()).iterator();
UncheckedIOException firstException = null;
while (iterator.hasNext()) {
AutoCloseable openFile = iterator.next();
try {
openFile.close();
} catch (UncheckedIOException e) {
if (firstException == null) {
firstException = e;
} else {
firstException.addSuppressed(e);
}
} catch (Exception e) {
LOG.error("Unexpected exception during close on " + openFile.getClass().getSimpleName(), e);
}
}
if (firstException != null) {
throw firstException;
}
}
}

View File

@@ -1,112 +0,0 @@
package org.cryptomator.filesystem;
import java.io.FileNotFoundException;
import java.io.UncheckedIOException;
import java.util.Arrays;
import java.util.Iterator;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
final class PathResolver {
private static final String DOT = ".";
private static final String DOTDOT = "..";
private PathResolver() {
}
/**
* Resolves a relative path (separated by '/') to a folder, e.g.
* <!-- @formatter:off -->
* <table>
* <thead>
* <tr>
* <th>dir</th>
* <th>path</th>
* <th>result</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>/foo/bar</td>
* <td>foo/bar</td>
* <td>/foo/bar/foo/bar</td>
* </tr>
* <tr>
* <td>/foo/bar</td>
* <td>../baz</td>
* <td>/foo/baz</td>
* </tr>
* <tr>
* <td>/foo/bar</td>
* <td>./foo/..</td>
* <td>/foo/bar</td>
* </tr>
* <tr>
* <td>/foo/bar</td>
* <td>/</td>
* <td>/foo/bar</td>
* </tr>
* <tr>
* <td>/foo/bar</td>
* <td></td>
* <td>/foo/bar</td>
* </tr>
* <tr>
* <td>/foo/bar</td>
* <td>../../..</td>
* <td>Exception</td>
* </tr>
* </tbody>
* </table>
*
* @param dir The directory from which to resolve the path.
* @param relativePath The path relative to a given directory.
* @return The folder with the given path relative to the given dir.
*/
public static Folder resolveFolder(Folder dir, String relativePath) {
final String[] fragments = StringUtils.split(relativePath, '/');
if (ArrayUtils.isEmpty(fragments)) {
return dir;
}
return resolveFolder(dir, Arrays.stream(fragments).iterator());
}
/**
* Resolves a relative path (separated by '/') to a file. Besides returning a File, this method is identical to {@link #resolveFile(Folder, String)}.
*
* @param dir The directory from which to resolve the path.
* @param relativePath The path relative to a given directory.
* @return The file with the given path relative to the given dir.
* @throws IllegalArgumentException
* if relativePath is empty, as this path would resolve to the directory itself, which obviously can't be a file.
*/
public static File resolveFile(Folder dir, String relativePath) {
final String[] fragments = StringUtils.split(relativePath, '/');
if (ArrayUtils.isEmpty(fragments)) {
throw new IllegalArgumentException("Empty relativePath");
}
final Folder folder = resolveFolder(dir, Arrays.stream(fragments).limit(fragments.length - 1).iterator());
final String filename = fragments[fragments.length - 1];
return folder.file(filename);
}
private static Folder resolveFolder(Folder dir, Iterator<String> remainingPathFragments) {
if (!remainingPathFragments.hasNext()) {
return dir;
}
final String fragment = remainingPathFragments.next();
assert fragment.length() > 0 : "iterator must not contain empty fragments";
if (DOT.equals(fragment)) {
return resolveFolder(dir, remainingPathFragments);
} else if (DOTDOT.equals(fragment) && dir.parent().isPresent()) {
return resolveFolder(dir.parent().get(), remainingPathFragments);
} else if (DOTDOT.equals(fragment) && !dir.parent().isPresent()) {
throw new UncheckedIOException(new FileNotFoundException("Unresolvable path"));
} else {
return resolveFolder(dir.folder(fragment), remainingPathFragments);
}
}
}

View File

@@ -1,57 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Markus Kreusch
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
******************************************************************************/
package org.cryptomator.filesystem;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
public interface ReadableFile extends ReadableByteChannel {
/**
* <p>
* Tries to fill the remaining space in the given byte buffer with data from
* this readable bytes from the current position.
* <p>
* May read less bytes if the end of this readable bytes has been reached.
*
* @param target
* the byte buffer to fill
* @return the number of bytes actually read, or {@code -1} if the end of
* file has been reached
* @throws UncheckedIOException
* if an {@link IOException} occurs while reading from this
* {@code ReadableBytes}
*/
@Override
int read(ByteBuffer target) throws UncheckedIOException;
/**
* @return The current size of the file. This value is a snapshot and might have been changed by concurrent modifications.
* @throws UncheckedIOException
* if an {@link IOException} occurs
*/
long size() throws UncheckedIOException;
/**
* <p>
* Fast-forwards or rewinds the file to the specified position.
* <p>
* Consecutive reads on the file will begin at the new position.
*
* @param position
* the position to set the file to
* @throws UncheckedIOException
* if an {@link IOException} occurs
*
*/
void position(long position) throws UncheckedIOException;
@Override
void close() throws UncheckedIOException;
}

View File

@@ -1,63 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Markus Kreusch
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
******************************************************************************/
package org.cryptomator.filesystem;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
public interface WritableFile extends WritableByteChannel {
void truncate() throws UncheckedIOException;
/**
* Writes the data in the given byte buffer to this readable bytes at the
* current position.
*
* @param source
* the byte buffer to use
* @return the number of bytes written, always equal to
* {@code source.remaining()}
* @throws UncheckedIOException
* if an {@link IOException} occurs while writing
*/
@Override
int write(ByteBuffer source) throws UncheckedIOException;
/**
* <p>
* Fast-forwards or rewinds the file to the specified position.
* <p>
* Consecutive writes on the file will begin at the new position.
* <p>
* If the position is set to a value greater than the current end of file
* consecutive writes will write data to the given position. The value of
* all bytes between this position and the previous end of file will be
* unspecified.
*
* @param position
* the position to set the file to
* @throws UncheckedIOException
* if an {@link IOException} occurs
*/
void position(long position) throws UncheckedIOException;
/**
* <p>
* Closes this {@code WritableFile} which finally commits all operations
* performed on it to the underlying file system.
* <p>
* After a {@code WritableFile} has been closed all other operations will
* throw an {@link UncheckedIOException}.
* <p>
* Invoking this method on a {@link WritableFile} which has already been
* closed does nothing.
*/
@Override
void close() throws UncheckedIOException;
}

View File

@@ -1,77 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.delegating;
import java.io.UncheckedIOException;
import java.util.Optional;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
public abstract class DelegatingFile<D extends DelegatingFolder<D, ?>> extends DelegatingNode<File>implements File {
private final D parent;
public DelegatingFile(D parent, File delegate) {
super(delegate);
this.parent = parent;
}
@Override
public Optional<D> parent() throws UncheckedIOException {
return Optional.of(parent);
}
@Override
public ReadableFile openReadable() throws UncheckedIOException {
return delegate.openReadable();
}
@Override
public WritableFile openWritable() throws UncheckedIOException {
return delegate.openWritable();
}
@Override
public void copyTo(File destination) {
if (getClass().equals(destination.getClass())) {
final File delegateDest = ((DelegatingFile<?>) destination).delegate;
delegate.copyTo(delegateDest);
} else {
delegate.copyTo(destination);
}
}
@Override
public void moveTo(File destination) {
if (getClass().equals(destination.getClass())) {
final File delegateDest = ((DelegatingFile<?>) destination).delegate;
delegate.moveTo(delegateDest);
} else {
throw new IllegalArgumentException("Can only move DelegatingFile to other DelegatingFile.");
}
}
@Override
public void delete() throws UncheckedIOException {
delegate.delete();
}
@Override
public int compareTo(File o) {
if (getClass().equals(o.getClass())) {
final File delegateOther = ((DelegatingFile<?>) o).delegate;
return delegate.compareTo(delegateOther);
} else {
return delegate.compareTo(o);
}
}
}

View File

@@ -1,22 +0,0 @@
package org.cryptomator.filesystem.delegating;
import java.util.Optional;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
public interface DelegatingFileSystem extends FileSystem {
Folder getDelegate();
@Override
default Optional<Long> quotaUsedBytes() {
return getDelegate().fileSystem().quotaUsedBytes();
}
@Override
default Optional<Long> quotaAvailableBytes() {
return getDelegate().fileSystem().quotaAvailableBytes();
}
}

View File

@@ -1,100 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.delegating;
import java.io.UncheckedIOException;
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;
public abstract class DelegatingFolder<D extends DelegatingFolder<D, F>, F extends DelegatingFile<D>> extends DelegatingNode<Folder>implements Folder {
private final D parent;
private final WeakValuedCache<Folder, D> folders = WeakValuedCache.usingLoader(this::newFolder);
private final WeakValuedCache<File, F> files = WeakValuedCache.usingLoader(this::newFile);
public DelegatingFolder(D parent, Folder delegate) {
super(delegate);
this.parent = parent;
}
@Override
public Optional<D> parent() throws UncheckedIOException {
return Optional.ofNullable(parent);
}
@Override
public Stream<? extends Node> children() throws UncheckedIOException {
return AutoClosingStream.from(Stream.concat(folders(), files()));
}
@Override
public Stream<D> folders() {
return delegate.folders().map(folders::get);
}
@Override
public Stream<F> files() throws UncheckedIOException {
return delegate.files().map(files::get);
}
@Override
public F file(String name) throws UncheckedIOException {
return files.get(delegate.file(name));
}
protected abstract F newFile(File delegate);
@Override
public D folder(String name) throws UncheckedIOException {
return folders.get(delegate.folder(name));
}
protected abstract D newFolder(Folder delegate);
@Override
public void create() throws UncheckedIOException {
if (exists()) {
return;
}
parent().ifPresent(p -> p.create());
delegate.create();
}
@Override
public void delete() {
delegate.delete();
}
@Override
public void copyTo(Folder destination) throws UncheckedIOException {
if (destination instanceof DelegatingFolder) {
final Folder delegateDest = ((DelegatingFolder<?, ?>) destination).delegate;
delegate.copyTo(delegateDest);
} else {
throw new IllegalArgumentException("Can only copy DelegatingFolder to other DelegatingFolder.");
}
}
@Override
public void moveTo(Folder destination) {
if (getClass().equals(destination.getClass())) {
final Folder delegateDest = ((DelegatingFolder<?, ?>) destination).delegate;
delegate.moveTo(delegateDest);
} else {
throw new IllegalArgumentException("Can only move DelegatingFolder to other DelegatingFolder.");
}
}
}

View File

@@ -1,78 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.delegating;
import java.io.UncheckedIOException;
import java.time.Instant;
import java.util.Optional;
import org.cryptomator.filesystem.Node;
public abstract class DelegatingNode<T extends Node> implements Node {
protected final T delegate;
public DelegatingNode(T delegate) {
if (delegate == null) {
throw new IllegalArgumentException("Delegate must not be null");
}
this.delegate = delegate;
}
@Override
public String name() throws UncheckedIOException {
return delegate.name();
}
@Override
public boolean exists() throws UncheckedIOException {
return delegate.exists();
}
@Override
public Instant lastModified() throws UncheckedIOException {
return delegate.lastModified();
}
@Override
public void setLastModified(Instant instant) throws UncheckedIOException {
delegate.setLastModified(instant);
}
@Override
public Optional<Instant> creationTime() throws UncheckedIOException {
return delegate.creationTime();
}
@Override
public void setCreationTime(Instant instant) throws UncheckedIOException {
delegate.setCreationTime(instant);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DelegatingNode) {
DelegatingNode<?> other = (DelegatingNode<?>) obj;
return this.delegate.equals(other.delegate);
} else {
return false;
}
}
@Override
public String toString() {
return "Delegate[" + delegate + "]";
}
}

View File

@@ -1,49 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.delegating;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import org.cryptomator.filesystem.ReadableFile;
public class DelegatingReadableFile implements ReadableFile {
private final ReadableFile delegate;
public DelegatingReadableFile(ReadableFile delegate) {
this.delegate = delegate;
}
@Override
public boolean isOpen() {
return delegate.isOpen();
}
@Override
public int read(ByteBuffer target) throws UncheckedIOException {
return delegate.read(target);
}
@Override
public long size() throws UncheckedIOException {
return delegate.size();
}
@Override
public void position(long position) throws UncheckedIOException {
delegate.position(position);
}
@Override
public void close() throws UncheckedIOException {
delegate.close();
}
}

View File

@@ -1,49 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.delegating;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import org.cryptomator.filesystem.WritableFile;
public class DelegatingWritableFile implements WritableFile {
final WritableFile delegate;
public DelegatingWritableFile(WritableFile delegate) {
this.delegate = delegate;
}
@Override
public boolean isOpen() {
return delegate.isOpen();
}
@Override
public void truncate() throws UncheckedIOException {
delegate.truncate();
}
@Override
public int write(ByteBuffer source) throws UncheckedIOException {
return delegate.write(source);
}
@Override
public void position(long position) throws UncheckedIOException {
delegate.position(position);
}
@Override
public void close() throws UncheckedIOException {
delegate.close();
}
}

View File

@@ -1,13 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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
*******************************************************************************/
/**
* Defines a file system abstraction to allow access to real and virtual file
* systems through a common API.
*/
package org.cryptomator.filesystem;

View File

@@ -1,35 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.io;
import java.nio.ByteBuffer;
public final class ByteBuffers {
private ByteBuffers() {
}
/**
* Copies as many bytes as possible from the given source to the destination buffer.
* The position of both buffers will be incremented by as many bytes as have been copied.
*
* @param source ByteBuffer from which bytes are read
* @param destination ByteBuffer into which bytes are written
* @return number of bytes copied, i.e. {@link ByteBuffer#remaining() source.remaining()} or {@link ByteBuffer#remaining() destination.remaining()}, whatever is less.
*/
public static int copy(ByteBuffer source, ByteBuffer destination) {
final int numBytes = Math.min(source.remaining(), destination.remaining());
final ByteBuffer tmp = source.asReadOnlyBuffer();
tmp.limit(tmp.position() + numBytes);
destination.put(tmp);
source.position(tmp.position()); // until now only tmp pos has been incremented, so we need to adjust the position
return numBytes;
}
}

View File

@@ -1,56 +0,0 @@
package org.cryptomator.io;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.nio.channels.Channels;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.WritableFile;
public final class FileContents {
public static final FileContents UTF_8 = FileContents.withCharset(StandardCharsets.UTF_8);
private final Charset charset;
private FileContents(Charset charset) {
this.charset = charset;
}
/**
* Reads the whole content from the given file.
*
* @param file File whose content should be read.
* @return The file's content interpreted in this FileContents' charset.
*/
public String readContents(File file) {
try (Reader reader = Channels.newReader(file.openReadable(), charset.newDecoder(), -1)) {
return IOUtils.toString(reader);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
* Writes the string into the file encoded with this FileContents' charset.
* This methods replaces any previously existing content, i.e. the string will be the sole content.
*
* @param file File whose content should be written.
* @param content The new content.
*/
public void writeContents(File file, String content) {
try (WritableFile writable = file.openWritable()) {
writable.truncate();
writable.write(charset.encode(content));
}
}
public static FileContents withCharset(Charset charset) {
return new FileContents(charset);
}
}

View File

@@ -1,36 +0,0 @@
package org.cryptomator.filesystem;
import java.nio.ByteBuffer;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;
public class ByteBufferMatcher {
public static Matcher<ByteBuffer> byteBufferFilledWith(int value) {
if (((byte) value) != value) {
throw new IllegalArgumentException("Invalid byte value");
}
return new TypeSafeDiagnosingMatcher<ByteBuffer>(ByteBuffer.class) {
@Override
public void describeTo(Description description) {
description.appendText("a byte buffer filled with " + value);
}
@Override
protected boolean matchesSafely(ByteBuffer item, Description mismatchDescription) {
while (item.hasRemaining()) {
byte currentValue = item.get();
if (currentValue != value) {
mismatchDescription.appendText("a byte buffer containing also " + currentValue);
return false;
}
}
return true;
}
};
}
}

View File

@@ -1,227 +0,0 @@
package org.cryptomator.filesystem;
import static java.util.Arrays.asList;
import static org.cryptomator.common.test.matcher.ContainsMatcher.contains;
import static org.cryptomator.common.test.mockito.Answers.collectParameters;
import static org.cryptomator.common.test.mockito.Answers.consecutiveAnswers;
import static org.cryptomator.common.test.mockito.Answers.value;
import static org.cryptomator.filesystem.File.EOF;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.stubbing.Answer;
import de.bechte.junit.runners.context.HierarchicalContextRunner;
@RunWith(HierarchicalContextRunner.class)
public class CopierTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
public class CopyFiles {
@Mock
private File source;
@Mock
private File destination;
@Mock
private ReadableFile readable;
@Mock
private WritableFile writable;
@Before
public void setUp() {
when(source.openReadable()).thenReturn(readable);
when(destination.openWritable()).thenReturn(writable);
}
@Test
public void testCopyFileOpensFilesInSortedOrderIfSourceIsSmallerDestination() {
mockCompareToWithOrder(source, destination);
when(readable.read(any())).thenReturn(EOF);
Copier.copy(source, destination);
InOrder inOrder = inOrder(source, destination);
inOrder.verify(source).openReadable();
inOrder.verify(destination).openWritable();
}
@Test
public void testCopyFileOpensFilesInSortedOrderIfDestinationIsSmallerSource() {
mockCompareToWithOrder(destination, source);
when(readable.read(any())).thenReturn(EOF);
Copier.copy(source, destination);
InOrder inOrder = inOrder(source, destination);
inOrder.verify(destination).openWritable();
inOrder.verify(source).openReadable();
}
@Test
public void testCopyFileReadsAndWritesReadableSourceAndWritableDestintationUntilEof() {
int irrelevantValue = 0;
Collection<byte[]> written = new ArrayList<>();
mockCompareToWithOrder(source, destination);
byte[] read1 = {1, 48, 32, 33, 22};
byte[] read2 = {4, 3, 1, -2, -8};
when(readable.read(any())).then(consecutiveAnswers(fillBufferWith(read1), fillBufferWith(read2), value(EOF)));
when(writable.write(any())).then(collectParameters(value(irrelevantValue), (ByteBuffer buffer) -> {
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
written.add(data);
}));
Copier.copy(source, destination);
InOrder inOrder = inOrder(readable, writable);
inOrder.verify(writable).truncate();
inOrder.verify(readable).read(any());
inOrder.verify(writable).write(any());
inOrder.verify(readable).read(any());
inOrder.verify(writable).write(any());
inOrder.verify(readable).read(any());
inOrder.verify(readable).close();
inOrder.verify(writable).close();
assertThat(written, contains(is(read1), is(read2)));
}
private Answer<Integer> fillBufferWith(byte[] data) {
return new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocation) throws Throwable {
ByteBuffer buffer = invocation.getArgumentAt(0, ByteBuffer.class);
for (byte value : data) {
buffer.put(value);
}
return data.length;
}
};
}
private void mockCompareToWithOrder(File first, File last) {
when(first.compareTo(last)).thenReturn(-1);
when(last.compareTo(first)).thenReturn(1);
}
}
public class CopyFolders {
@Mock
private Folder source;
@Mock
private Folder destination;
@Test
public void testCopyFolderDeletesAndCreatesDestinationBeforeIteratingOverTheFilesAndFoldersInSource() {
when(source.files()).thenReturn(Stream.empty());
when(source.folders()).thenReturn(Stream.empty());
Copier.copy(source, destination);
InOrder inOrder = inOrder(source, destination);
inOrder.verify(destination).delete();
inOrder.verify(destination).create();
inOrder.verify(source).files();
inOrder.verify(source).folders();
}
@Test
@SuppressWarnings({"unchecked", "rawtypes"})
public void testCopyFolderInvokesCopyToOnAllFilesInSourceWithFileWithSameNameFromDestination() {
String filename1 = "nameOfFile1";
String filename2 = "nameOfFile2";
File file1 = mock(File.class);
File file2 = mock(File.class);
File destinationFile1 = mock(File.class);
File destinationFile2 = mock(File.class);
when(source.files()).thenReturn((Stream) asList(file1, file2).stream());
when(source.folders()).thenReturn(Stream.empty());
when(destination.file(filename1)).thenReturn(destinationFile1);
when(destination.file(filename2)).thenReturn(destinationFile2);
when(file1.name()).thenReturn(filename1);
when(file2.name()).thenReturn(filename2);
Copier.copy(source, destination);
verify(file1).copyTo(destinationFile1);
verify(file2).copyTo(destinationFile2);
}
@Test
@SuppressWarnings({"unchecked", "rawtypes"})
public void testCopyFolderInvokesCopyToOnAllFoldersInSourceWithFolderWithSameNameFromDestination() {
String folderName1 = "nameOfFolder1";
String folderName2 = "nameOfFolder2";
Folder folder1 = mock(Folder.class);
Folder folder2 = mock(Folder.class);
Folder destinationfolder1 = mock(Folder.class);
Folder destinationfolder2 = mock(Folder.class);
when(source.folders()).thenReturn((Stream) asList(folder1, folder2).stream());
when(source.files()).thenReturn(Stream.empty());
when(destination.folder(folderName1)).thenReturn(destinationfolder1);
when(destination.folder(folderName2)).thenReturn(destinationfolder2);
when(folder1.name()).thenReturn(folderName1);
when(folder2.name()).thenReturn(folderName2);
Copier.copy(source, destination);
verify(folder1).copyTo(destinationfolder1);
verify(folder2).copyTo(destinationfolder2);
}
@Test
public void testCopyFolderFailsWithIllegalArgumentExceptionIfSourceIsNestedInDestination() {
when(source.isAncestorOf(destination)).thenReturn(false);
when(destination.isAncestorOf(source)).thenReturn(true);
thrown.expect(IllegalArgumentException.class);
Copier.copy(source, destination);
}
@Test
public void testCopyFolderFailsWithIllegalArgumentExceptionIfDestinationIsNestedInSource() {
when(source.isAncestorOf(destination)).thenReturn(true);
when(destination.isAncestorOf(source)).thenReturn(false);
thrown.expect(IllegalArgumentException.class);
Copier.copy(source, destination);
}
}
}

View File

@@ -1,70 +0,0 @@
package org.cryptomator.filesystem;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Optional;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class PathResolverTest {
private final Folder root = Mockito.mock(Folder.class);
private final Folder foo = Mockito.mock(Folder.class);
private final Folder bar = Mockito.mock(Folder.class);
private final File baz = Mockito.mock(File.class);
@Before
public void configureMocks() throws IOException {
Mockito.doReturn(Optional.empty()).when(root).parent();
Mockito.doReturn(Optional.of(root)).when(foo).parent();
Mockito.doReturn(Optional.of(foo)).when(bar).parent();
Mockito.doReturn(foo).when(root).folder("foo");
Mockito.doReturn(bar).when(foo).folder("bar");
Mockito.doReturn(baz).when(bar).file("baz");
}
@Test
public void testResolveSameFolder() {
Assert.assertEquals(foo, PathResolver.resolveFolder(foo, ""));
Assert.assertEquals(foo, PathResolver.resolveFolder(foo, "/"));
Assert.assertEquals(foo, PathResolver.resolveFolder(foo, "///"));
}
@Test
public void testResolveChildFolder() {
Assert.assertEquals(bar, PathResolver.resolveFolder(root, "foo/bar"));
Assert.assertEquals(bar, PathResolver.resolveFolder(root, "foo/./bar"));
Assert.assertEquals(bar, PathResolver.resolveFolder(root, "./foo/././bar"));
}
@Test
public void testResolveParentFolder() {
Assert.assertEquals(foo, PathResolver.resolveFolder(bar, ".."));
Assert.assertEquals(root, PathResolver.resolveFolder(bar, "../.."));
}
@Test
public void testResolveSiblingFolder() {
Assert.assertEquals(foo, PathResolver.resolveFolder(bar, "../../foo"));
}
@Test(expected = UncheckedIOException.class)
public void testResolveUnresolvableFolder() {
PathResolver.resolveFolder(root, "..");
}
@Test(expected = IllegalArgumentException.class)
public void testResolveFileWithEmptyPath() {
PathResolver.resolveFile(root, "");
}
@Test
public void testResolveFile() {
Assert.assertEquals(baz, PathResolver.resolveFile(foo, "../foo/bar/./baz"));
}
}

View File

@@ -1,32 +0,0 @@
package org.cryptomator.filesystem.delegating;
import java.util.Optional;
import org.cryptomator.filesystem.FileSystem;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class DelegatingFileSystemTest {
@Test
public void testQuotaAvailableBytes() {
FileSystem mockFs = Mockito.mock(FileSystem.class);
Mockito.when(mockFs.fileSystem()).thenReturn(mockFs);
Mockito.when(mockFs.quotaAvailableBytes()).thenReturn(Optional.of(42l));
DelegatingFileSystem delegatingFs = TestDelegatingFileSystem.withRoot(mockFs);
Assert.assertEquals(mockFs.quotaAvailableBytes(), delegatingFs.quotaAvailableBytes());
}
@Test
public void testQuotaUsedBytes() {
FileSystem mockFs = Mockito.mock(FileSystem.class);
Mockito.when(mockFs.fileSystem()).thenReturn(mockFs);
Mockito.when(mockFs.quotaUsedBytes()).thenReturn(Optional.of(23l));
DelegatingFileSystem delegatingFs = TestDelegatingFileSystem.withRoot(mockFs);
Assert.assertEquals(mockFs.quotaUsedBytes(), delegatingFs.quotaUsedBytes());
}
}

View File

@@ -1,176 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.delegating;
import java.time.Instant;
import java.util.Optional;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class DelegatingFileTest {
@Test
public void testName() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Mockito.when(mockFile.name()).thenReturn("Test");
Assert.assertEquals(mockFile.name(), delegatingFile.name());
}
@Test
public void testParent() {
Folder mockFolder = Mockito.mock(Folder.class);
File mockFile = Mockito.mock(File.class);
TestDelegatingFileSystem delegatingParent = TestDelegatingFileSystem.withRoot(mockFolder);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(delegatingParent, mockFile);
Assert.assertEquals(delegatingParent, delegatingFile.parent().get());
}
@Test
public void testExists() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Mockito.when(mockFile.exists()).thenReturn(true);
Assert.assertTrue(delegatingFile.exists());
Mockito.when(mockFile.exists()).thenReturn(false);
Assert.assertFalse(delegatingFile.exists());
}
@Test
public void testLastModified() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Instant now = Instant.now();
Mockito.when(mockFile.lastModified()).thenReturn(now);
Assert.assertEquals(now, delegatingFile.lastModified());
}
@Test
public void testSetLastModified() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Instant now = Instant.now();
delegatingFile.setLastModified(now);
Mockito.verify(mockFile).setLastModified(now);
}
@Test
public void testCreationTime() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Instant now = Instant.now();
Mockito.when(mockFile.creationTime()).thenReturn(Optional.of(now));
Assert.assertEquals(now, delegatingFile.creationTime().get());
}
@Test
public void testSetCreationTime() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Instant now = Instant.now();
delegatingFile.setCreationTime(now);
Mockito.verify(mockFile).setCreationTime(now);
}
@Test
public void testOpenReadable() {
File mockFile = Mockito.mock(File.class);
ReadableFile mockReadableFile = Mockito.mock(ReadableFile.class);
Mockito.when(mockFile.openReadable()).thenReturn(mockReadableFile);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Assert.assertNotNull(delegatingFile.openReadable());
}
@Test
public void testOpenWritable() {
File mockFile = Mockito.mock(File.class);
WritableFile mockWritableFile = Mockito.mock(WritableFile.class);
Mockito.when(mockFile.openWritable()).thenReturn(mockWritableFile);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Assert.assertNotNull(delegatingFile.openWritable());
}
@Test
public void testMoveTo() {
File mockFile1 = Mockito.mock(File.class);
File mockFile2 = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile1 = new TestDelegatingFile(null, mockFile1);
DelegatingFile<?> delegatingFile2 = new TestDelegatingFile(null, mockFile2);
delegatingFile1.moveTo(delegatingFile2);
Mockito.verify(mockFile1).moveTo(mockFile2);
}
@Test(expected = IllegalArgumentException.class)
public void testMoveToDestinationFromDifferentLayer() {
File mockFile1 = Mockito.mock(File.class);
File mockFile2 = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile1 = new TestDelegatingFile(null, mockFile1);
delegatingFile1.moveTo(mockFile2);
}
@Test
public void testCopyTo() {
File mockFile1 = Mockito.mock(File.class);
File mockFile2 = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile1 = new TestDelegatingFile(null, mockFile1);
DelegatingFile<?> delegatingFile2 = new TestDelegatingFile(null, mockFile2);
delegatingFile1.copyTo(delegatingFile2);
Mockito.verify(mockFile1).copyTo(mockFile2);
}
@Test
public void testCopyToDestinationFromDifferentLayer() {
File mockFile1 = Mockito.mock(File.class);
File mockFile2 = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile1 = new TestDelegatingFile(null, mockFile1);
delegatingFile1.copyTo(mockFile2);
Mockito.verify(mockFile1).copyTo(mockFile2);
}
@Test
public void testDelete() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
delegatingFile.delete();
Mockito.verify(mockFile).delete();
}
@Test
public void testCompareTo() {
File mockFile1 = Mockito.mock(File.class);
File mockFile2 = Mockito.mock(File.class);
Mockito.when(mockFile1.compareTo(mockFile2)).thenReturn(-1);
DelegatingFile<?> delegatingFile1 = new TestDelegatingFile(null, mockFile1);
DelegatingFile<?> delegatingFile2 = new TestDelegatingFile(null, mockFile2);
Assert.assertEquals(-1, delegatingFile1.compareTo(delegatingFile2));
}
}

View File

@@ -1,206 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.delegating;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class DelegatingFolderTest {
@Test
public void testName() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Mockito.when(mockFolder.name()).thenReturn("Test");
Assert.assertEquals(mockFolder.name(), delegatingFolder.name());
}
@Test
public void testParent() {
Folder mockFolder1 = Mockito.mock(Folder.class);
Folder mockFolder2 = Mockito.mock(Folder.class);
TestDelegatingFileSystem delegatingParent = TestDelegatingFileSystem.withRoot(mockFolder1);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(delegatingParent, mockFolder2);
Assert.assertEquals(delegatingParent, delegatingFolder.parent().get());
}
@Test
public void testExists() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Mockito.when(mockFolder.exists()).thenReturn(true);
Assert.assertTrue(delegatingFolder.exists());
Mockito.when(mockFolder.exists()).thenReturn(false);
Assert.assertFalse(delegatingFolder.exists());
}
@Test
public void testLastModified() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Instant now = Instant.now();
Mockito.when(mockFolder.lastModified()).thenReturn(now);
Assert.assertEquals(now, delegatingFolder.lastModified());
}
@Test
public void testSetLastModified() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Instant now = Instant.now();
delegatingFolder.setLastModified(now);
Mockito.verify(mockFolder).setLastModified(now);
}
@Test
public void testCreationTime() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Instant now = Instant.now();
Mockito.when(mockFolder.creationTime()).thenReturn(Optional.of(now));
Assert.assertEquals(now, delegatingFolder.creationTime().get());
}
@Test
public void testSetCreationTime() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Instant now = Instant.now();
delegatingFolder.setCreationTime(now);
Mockito.verify(mockFolder).setCreationTime(now);
}
@Test
public void testChildren() {
Folder mockFolder = Mockito.mock(Folder.class);
TestDelegatingFileSystem delegatingFolder = TestDelegatingFileSystem.withRoot(mockFolder);
Folder subFolder1 = Mockito.mock(Folder.class);
TestDelegatingFolder delegatingSubFolder1 = new TestDelegatingFolder(delegatingFolder, subFolder1);
File subFile1 = Mockito.mock(File.class);
TestDelegatingFile delegatingSubFile1 = new TestDelegatingFile(delegatingFolder, subFile1);
/* folders */
Mockito.when(mockFolder.folder("subFolder1")).thenReturn(subFolder1);
Assert.assertEquals(delegatingSubFolder1, delegatingFolder.folder("subFolder1"));
Mockito.<Stream<? extends Folder>>when(mockFolder.folders()).thenAnswer((invocation) -> {
return Arrays.stream(new Folder[] {subFolder1});
});
List<TestDelegatingFolder> subFolders = delegatingFolder.folders().collect(Collectors.toList());
Assert.assertThat(subFolders, Matchers.containsInAnyOrder(delegatingSubFolder1));
/* files */
Mockito.when(mockFolder.file("subFile1")).thenReturn(subFile1);
Assert.assertEquals(delegatingSubFile1, delegatingFolder.file("subFile1"));
Mockito.<Stream<? extends File>>when(mockFolder.files()).thenAnswer((invocation) -> {
return Arrays.stream(new File[] {subFile1});
});
List<TestDelegatingFile> subFiles = delegatingFolder.files().collect(Collectors.toList());
Assert.assertThat(subFiles, Matchers.containsInAnyOrder(delegatingSubFile1));
/* files and folders */
List<Node> children = delegatingFolder.children().collect(Collectors.toList());
DelegatingNode<?>[] expectedChildren = new DelegatingNode[] {delegatingSubFolder1, delegatingSubFile1};
Assert.assertThat(children, Matchers.containsInAnyOrder(expectedChildren));
}
@Test
public void testMoveTo() {
Folder mockFolder1 = Mockito.mock(Folder.class);
Folder mockFolder2 = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder1 = new TestDelegatingFolder(null, mockFolder1);
DelegatingFolder<?, ?> delegatingFolder2 = new TestDelegatingFolder(null, mockFolder2);
delegatingFolder1.moveTo(delegatingFolder2);
Mockito.verify(mockFolder1).moveTo(mockFolder2);
}
@Test(expected = IllegalArgumentException.class)
public void testMoveToDestinationFromDifferentLayer() {
Folder mockFolder1 = Mockito.mock(Folder.class);
Folder mockFolder2 = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder1 = new TestDelegatingFolder(null, mockFolder1);
delegatingFolder1.moveTo(mockFolder2);
}
@Test
public void testCopyTo() {
Folder mockFolder1 = Mockito.mock(Folder.class);
Folder mockFolder2 = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder1 = new TestDelegatingFolder(null, mockFolder1);
DelegatingFolder<?, ?> delegatingFolder2 = new TestDelegatingFolder(null, mockFolder2);
delegatingFolder1.copyTo(delegatingFolder2);
Mockito.verify(mockFolder1).copyTo(mockFolder2);
}
@Test(expected = IllegalArgumentException.class)
public void testCopyToDestinationFromDifferentLayer() {
Folder mockFolder1 = Mockito.mock(Folder.class);
Folder mockFolder2 = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder1 = new TestDelegatingFolder(null, mockFolder1);
delegatingFolder1.copyTo(mockFolder2);
}
@Test
public void testCreate() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
delegatingFolder.create();
Mockito.verify(mockFolder).create();
}
@Test
public void testDelete() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
delegatingFolder.delete();
Mockito.verify(mockFolder).delete();
}
@Test
public void testSubresourcesAreSameInstance() {
Folder mockFolder = Mockito.mock(Folder.class);
Folder mockSubFolder = Mockito.mock(Folder.class);
File mockSubFile = Mockito.mock(File.class);
Mockito.when(mockFolder.folder("mockSubFolder")).thenReturn(mockSubFolder);
Mockito.when(mockFolder.file("mockSubFile")).thenReturn(mockSubFile);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Assert.assertSame(delegatingFolder.folder("mockSubFolder"), delegatingFolder.folder("mockSubFolder"));
Assert.assertSame(delegatingFolder.file("mockSubFile"), delegatingFolder.file("mockSubFile"));
}
}

View File

@@ -1,75 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.delegating;
import java.nio.ByteBuffer;
import org.cryptomator.filesystem.ReadableFile;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class DelegatingReadableFileTest {
@Test
public void testIsOpen() {
ReadableFile mockReadableFile = Mockito.mock(ReadableFile.class);
@SuppressWarnings("resource")
DelegatingReadableFile delegatingReadableFile = new DelegatingReadableFile(mockReadableFile);
Mockito.when(mockReadableFile.isOpen()).thenReturn(true);
Assert.assertTrue(delegatingReadableFile.isOpen());
Mockito.when(mockReadableFile.isOpen()).thenReturn(false);
Assert.assertFalse(delegatingReadableFile.isOpen());
}
@Test
public void testRead() {
ReadableFile mockReadableFile = Mockito.mock(ReadableFile.class);
@SuppressWarnings("resource")
DelegatingReadableFile delegatingReadableFile = new DelegatingReadableFile(mockReadableFile);
ByteBuffer buf = ByteBuffer.allocate(4);
Mockito.when(mockReadableFile.read(buf)).thenReturn(4);
Assert.assertEquals(4, delegatingReadableFile.read(buf));
Mockito.verify(mockReadableFile).read(buf);
}
@Test
public void testSize() {
ReadableFile mockReadableFile = Mockito.mock(ReadableFile.class);
@SuppressWarnings("resource")
DelegatingReadableFile delegatingReadableFile = new DelegatingReadableFile(mockReadableFile);
Mockito.when(mockReadableFile.size()).thenReturn(42l);
Assert.assertEquals(42l, delegatingReadableFile.size());
Mockito.verify(mockReadableFile).size();
}
@Test
public void testPosition() {
ReadableFile mockReadableFile = Mockito.mock(ReadableFile.class);
@SuppressWarnings("resource")
DelegatingReadableFile delegatingReadableFile = new DelegatingReadableFile(mockReadableFile);
delegatingReadableFile.position(42);
Mockito.verify(mockReadableFile).position(42);
}
@Test
public void testClose() {
ReadableFile mockReadableFile = Mockito.mock(ReadableFile.class);
DelegatingReadableFile delegatingReadableFile = new DelegatingReadableFile(mockReadableFile);
delegatingReadableFile.close();
Mockito.verify(mockReadableFile).close();
}
}

View File

@@ -1,74 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.delegating;
import java.nio.ByteBuffer;
import org.cryptomator.filesystem.WritableFile;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class DelegatingWritableFileTest {
@Test
public void testIsOpen() {
WritableFile mockWritableFile = Mockito.mock(WritableFile.class);
@SuppressWarnings("resource")
DelegatingWritableFile delegatingWritableFile = new DelegatingWritableFile(mockWritableFile);
Mockito.when(mockWritableFile.isOpen()).thenReturn(true);
Assert.assertTrue(delegatingWritableFile.isOpen());
Mockito.when(mockWritableFile.isOpen()).thenReturn(false);
Assert.assertFalse(delegatingWritableFile.isOpen());
}
@Test
public void testTruncate() {
WritableFile mockWritableFile = Mockito.mock(WritableFile.class);
@SuppressWarnings("resource")
DelegatingWritableFile delegatingWritableFile = new DelegatingWritableFile(mockWritableFile);
delegatingWritableFile.truncate();
Mockito.verify(mockWritableFile).truncate();
}
@Test
public void testWrite() {
WritableFile mockWritableFile = Mockito.mock(WritableFile.class);
@SuppressWarnings("resource")
DelegatingWritableFile delegatingWritableFile = new DelegatingWritableFile(mockWritableFile);
ByteBuffer buf = ByteBuffer.allocate(4);
Mockito.when(mockWritableFile.write(buf)).thenReturn(4);
Assert.assertEquals(4, delegatingWritableFile.write(buf));
Mockito.verify(mockWritableFile).write(buf);
}
@Test
public void testPosition() {
WritableFile mockWritableFile = Mockito.mock(WritableFile.class);
@SuppressWarnings("resource")
DelegatingWritableFile delegatingWritableFile = new DelegatingWritableFile(mockWritableFile);
delegatingWritableFile.position(42);
Mockito.verify(mockWritableFile).position(42);
}
@Test
public void testClose() {
WritableFile mockWritableFile = Mockito.mock(WritableFile.class);
DelegatingWritableFile delegatingWritableFile = new DelegatingWritableFile(mockWritableFile);
delegatingWritableFile.close();
Mockito.verify(mockWritableFile).close();
}
}

View File

@@ -1,11 +0,0 @@
package org.cryptomator.filesystem.delegating;
import org.cryptomator.filesystem.File;
class TestDelegatingFile extends DelegatingFile<TestDelegatingFolder> {
public TestDelegatingFile(TestDelegatingFolder parent, File delegate) {
super(parent, delegate);
}
}

View File

@@ -1,20 +0,0 @@
package org.cryptomator.filesystem.delegating;
import org.cryptomator.filesystem.Folder;
class TestDelegatingFileSystem extends TestDelegatingFolder implements DelegatingFileSystem {
private TestDelegatingFileSystem(Folder delegate) {
super(null, delegate);
}
public static TestDelegatingFileSystem withRoot(Folder delegate) {
return new TestDelegatingFileSystem(delegate);
}
@Override
public Folder getDelegate() {
return delegate;
}
}

View File

@@ -1,22 +0,0 @@
package org.cryptomator.filesystem.delegating;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
class TestDelegatingFolder extends DelegatingFolder<TestDelegatingFolder, TestDelegatingFile> {
public TestDelegatingFolder(TestDelegatingFolder parent, Folder delegate) {
super(parent, delegate);
}
@Override
protected TestDelegatingFile newFile(File delegate) {
return new TestDelegatingFile(this, delegate);
}
@Override
protected TestDelegatingFolder newFolder(Folder delegate) {
return new TestDelegatingFolder(this, delegate);
}
}

View File

@@ -1,81 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.io;
import java.nio.ByteBuffer;
import org.junit.Assert;
import org.junit.Test;
public class ByteBuffersTest {
@Test
public void testCopyOfEmptySource() {
final ByteBuffer src = ByteBuffer.allocate(0);
final ByteBuffer dst = ByteBuffer.allocate(5);
dst.put(new byte[3]);
Assert.assertEquals(0, src.position());
Assert.assertEquals(0, src.remaining());
Assert.assertEquals(3, dst.position());
Assert.assertEquals(2, dst.remaining());
ByteBuffers.copy(src, dst);
Assert.assertEquals(0, src.position());
Assert.assertEquals(0, src.remaining());
Assert.assertEquals(3, dst.position());
Assert.assertEquals(2, dst.remaining());
}
@Test
public void testCopyToEmptyDestination() {
final ByteBuffer src = ByteBuffer.wrap(new byte[4]);
final ByteBuffer dst = ByteBuffer.allocate(0);
src.put(new byte[2]);
Assert.assertEquals(2, src.position());
Assert.assertEquals(2, src.remaining());
Assert.assertEquals(0, dst.position());
Assert.assertEquals(0, dst.remaining());
ByteBuffers.copy(src, dst);
Assert.assertEquals(2, src.position());
Assert.assertEquals(2, src.remaining());
Assert.assertEquals(0, dst.position());
Assert.assertEquals(0, dst.remaining());
}
@Test
public void testCopyToBiggerDestination() {
final ByteBuffer src = ByteBuffer.wrap(new byte[2]);
final ByteBuffer dst = ByteBuffer.allocate(10);
dst.put(new byte[3]);
Assert.assertEquals(0, src.position());
Assert.assertEquals(2, src.remaining());
Assert.assertEquals(3, dst.position());
Assert.assertEquals(7, dst.remaining());
ByteBuffers.copy(src, dst);
Assert.assertEquals(2, src.position());
Assert.assertEquals(0, src.remaining());
Assert.assertEquals(5, dst.position());
Assert.assertEquals(5, dst.remaining());
}
@Test
public void testCopyToSmallerDestination() {
final ByteBuffer src = ByteBuffer.wrap(new byte[5]);
final ByteBuffer dst = ByteBuffer.allocate(2);
Assert.assertEquals(0, src.position());
Assert.assertEquals(5, src.remaining());
Assert.assertEquals(0, dst.position());
Assert.assertEquals(2, dst.remaining());
ByteBuffers.copy(src, dst);
Assert.assertEquals(2, src.position());
Assert.assertEquals(3, src.remaining());
Assert.assertEquals(2, dst.position());
Assert.assertEquals(0, dst.remaining());
}
}

View File

@@ -1,103 +0,0 @@
package org.cryptomator.io;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@RunWith(Theories.class)
public class FileContentsTest {
@DataPoints
public static final Iterable<Charset> CHARSETS = Arrays.asList(StandardCharsets.UTF_8, StandardCharsets.US_ASCII, StandardCharsets.UTF_16);
@DataPoints
public static final Iterable<String> TEST_CONTENTS = Arrays.asList("hello world", "hellö wörld", "");
@Theory
public void testReadAll(Charset charset, String testString) {
Assume.assumeTrue(charset.newEncoder().canEncode(testString));
ByteBuffer testContent = ByteBuffer.wrap(testString.getBytes(charset));
File file = Mockito.mock(File.class);
ReadableFile readable = Mockito.mock(ReadableFile.class);
Mockito.when(file.openReadable()).thenReturn(readable);
Mockito.when(readable.read(Mockito.any(ByteBuffer.class))).then(invocation -> {
ByteBuffer target = invocation.getArgumentAt(0, ByteBuffer.class);
if (testContent.hasRemaining()) {
return ByteBuffers.copy(testContent, target);
} else {
return -1;
}
});
String contentsRead = FileContents.withCharset(charset).readContents(file);
Assert.assertEquals(testString, contentsRead);
}
@Theory
public void testWriteAll(Charset charset, String testString) {
Assume.assumeTrue(charset.newEncoder().canEncode(testString));
ByteBuffer testContent = ByteBuffer.allocate(100);
File file = Mockito.mock(File.class);
WritableFile writable = Mockito.mock(WritableFile.class);
Mockito.when(file.openWritable()).thenReturn(writable);
Mockito.doAnswer(invocation -> {
testContent.clear();
return null;
}).when(writable).truncate();
Mockito.when(writable.write(Mockito.any(ByteBuffer.class))).then(invocation -> {
ByteBuffer source = invocation.getArgumentAt(0, ByteBuffer.class);
if (testContent.hasRemaining()) {
return ByteBuffers.copy(source, testContent);
} else {
return -1;
}
});
FileContents.withCharset(charset).writeContents(file, testString);
Assert.assertArrayEquals(testString.getBytes(charset), Arrays.copyOf(testContent.array(), testContent.position()));
}
@Test(expected = UncheckedIOException.class)
public void testIOExceptionDuringRead() {
File file = Mockito.mock(File.class);
Mockito.when(file.openReadable()).thenAnswer(invocation -> {
throw new IOException("failed");
});
FileContents.UTF_8.readContents(file);
}
@Test(expected = UncheckedIOException.class)
public void testUncheckedIOExceptionDuringRead() {
File file = Mockito.mock(File.class);
Mockito.when(file.openReadable()).thenThrow(new UncheckedIOException(new IOException("failed")));
FileContents.UTF_8.readContents(file);
}
@Test(expected = UncheckedIOException.class)
public void testUncheckedIOExceptionDuringWrite() {
File file = Mockito.mock(File.class);
Mockito.when(file.openWritable()).thenThrow(new UncheckedIOException(new IOException("failed")));
FileContents.UTF_8.writeContents(file, "hello world");
}
}

View File

@@ -1,22 +0,0 @@
<?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

@@ -1,50 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 Markus Kreusch
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.3</version>
<version>1.2.4</version>
</parent>
<artifactId>filesystem-api</artifactId>
<name>Cryptomator filesystem: API</name>
<artifactId>filesystem-charsets</artifactId>
<name>Cryptomator filesystem: Charset compatibility layer</name>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>commons</artifactId>
</dependency>
<!-- Guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<artifactId>filesystem-api</artifactId>
</dependency>
<!-- apache commons -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</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>

View File

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

View File

@@ -1,33 +0,0 @@
/*******************************************************************************
* 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.crypto;
import java.security.SecureRandom;
import java.util.Arrays;
import org.cryptomator.crypto.engine.impl.CryptoEngineModule;
/**
* Used as drop-in-replacement for {@link CryptoEngineModule} during unit tests.
*/
public class CryptoEngineTestModule extends CryptoEngineModule {
@Override
public SecureRandom provideSecureRandom() {
return new SecureRandom() {
@Override
public void nextBytes(byte[] bytes) {
Arrays.fill(bytes, (byte) 0x00);
}
};
}
}

View File

@@ -1,32 +0,0 @@
/*******************************************************************************
* 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.crypto;
import javax.inject.Singleton;
import org.cryptomator.crypto.engine.impl.CryptoEngineModule;
import org.cryptomator.filesystem.shortening.ShorteningFileSystemFactory;
import dagger.Component;
/**
* To be used in integration tests, where a {@link CryptoFileSystem} is needed in conjunction with {@link CryptoEngineTestModule} (which mocks the CSPRNG) as follows:
* <code>
* DaggerCryptoFileSystemTestComponent.builder().cryptoEngineModule(new CryptoEngineTestModule()).build()
* </code>
*/
@Singleton
@Component(modules = CryptoEngineModule.class)
public interface CryptoFileSystemTestComponent {
CryptoFileSystemFactory cryptoFileSystemFactory();
ShorteningFileSystemFactory shorteningFileSystemFactory();
}

View File

@@ -1,298 +0,0 @@
/*******************************************************************************
* 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.crypto;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.Future;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CryptoFileSystemIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(CryptoFileSystemIntegrationTest.class);
private final CryptoFileSystemTestComponent cryptoFsComp = DaggerCryptoFileSystemTestComponent.builder().cryptoEngineModule(new CryptoEngineTestModule()).build();
private CryptoFileSystemDelegate cryptoDelegate;
private FileSystem ciphertextFs;
private FileSystem cleartextFs;
@Before
public void setupFileSystems() {
cryptoDelegate = Mockito.mock(CryptoFileSystemDelegate.class);
ciphertextFs = new InMemoryFileSystem();
FileSystem shorteningFs = cryptoFsComp.shorteningFileSystemFactory().get(ciphertextFs);
cryptoFsComp.cryptoFileSystemFactory().initializeNew(shorteningFs, "TopSecret");
cleartextFs = cryptoFsComp.cryptoFileSystemFactory().unlockExisting(shorteningFs, "TopSecret", cryptoDelegate);
}
@Test(timeout = 1000)
public void testVaultStructureInitializationAndBackupBehaviour() throws UncheckedIOException, IOException {
final FileSystem physicalFs = new InMemoryFileSystem();
final File masterkeyFile = physicalFs.file("masterkey.cryptomator");
final File masterkeyBkupFile = physicalFs.file("masterkey.cryptomator.bkup");
final Folder physicalDataRoot = physicalFs.folder("d");
Assert.assertFalse(masterkeyFile.exists());
Assert.assertFalse(masterkeyBkupFile.exists());
Assert.assertFalse(physicalDataRoot.exists());
cryptoFsComp.cryptoFileSystemFactory().initializeNew(physicalFs, "asd");
Assert.assertTrue(masterkeyFile.exists());
Assert.assertFalse(masterkeyBkupFile.exists());
Assert.assertFalse(physicalDataRoot.exists());
@SuppressWarnings("unused")
final FileSystem cryptoFs = cryptoFsComp.cryptoFileSystemFactory().unlockExisting(physicalFs, "asd", cryptoDelegate);
Assert.assertTrue(masterkeyBkupFile.exists());
Assert.assertTrue(physicalDataRoot.exists());
Assert.assertEquals(3, physicalFs.children().count()); // d + masterkey.cryptomator + masterkey.cryptomator.bkup
Assert.assertEquals(1, physicalDataRoot.folders().count()); // ROOT directory
}
@Test
public void testEncryptionOfLongFolderNames() {
final String shortName = "normal folder name";
final String longName = "this will be a long filename after encryption, because its encrypted name is longer than onehundredandeighty characters";
final Folder shortFolder = cleartextFs.folder(shortName);
final Folder longFolder = cleartextFs.folder(longName);
shortFolder.create();
longFolder.create();
// because of the long file, a metadata folder should exist on the physical layer:
Assert.assertEquals(1, ciphertextFs.folder("m").folders().count());
Assert.assertTrue(ciphertextFs.folder("m").exists());
// but the shortened filenames must not be visible on the cleartext layer:
Assert.assertArrayEquals(new String[] {shortName, longName}, cleartextFs.folders().map(Node::name).sorted().toArray());
}
@Test
public void testEncryptionAndDecryptionOfFiles() {
// write test content to encrypted file
try (WritableFile writable = cleartextFs.file("test1.txt").openWritable()) {
writable.write(ByteBuffer.wrap("Hello ".getBytes()));
writable.write(ByteBuffer.wrap("World".getBytes()));
}
File physicalFile = ciphertextFs.folder("d").folders().findAny().get().folders().findAny().get().files().findAny().get();
Assert.assertTrue(physicalFile.exists());
// read test content from decrypted file
try (ReadableFile readable = cleartextFs.file("test1.txt").openReadable()) {
ByteBuffer buf1 = ByteBuffer.allocate(5);
readable.read(buf1);
buf1.flip();
Assert.assertEquals("Hello", new String(buf1.array(), 0, buf1.remaining()));
ByteBuffer buf2 = ByteBuffer.allocate(10);
readable.read(buf2);
buf2.flip();
Assert.assertArrayEquals(" World".getBytes(), Arrays.copyOfRange(buf2.array(), 0, buf2.remaining()));
}
}
@Test
public void testForcedDecryptionOfManipulatedFile() {
// write test content to encrypted file
try (WritableFile writable = cleartextFs.file("test1.txt").openWritable()) {
writable.write(ByteBuffer.wrap("Hello World".getBytes()));
}
File physicalFile = ciphertextFs.folder("d").folders().findAny().get().folders().findAny().get().files().findAny().get();
Assert.assertTrue(physicalFile.exists());
// toggle last bit
try (WritableFile writable = physicalFile.openWritable(); ReadableFile readable = physicalFile.openReadable()) {
ByteBuffer buf = ByteBuffer.allocate((int) readable.size());
readable.read(buf);
buf.array()[buf.limit() - 1] ^= 0x01;
buf.flip();
writable.write(buf);
}
// whitelist
Mockito.when(cryptoDelegate.shouldSkipAuthentication("/test1.txt")).thenReturn(true);
// read test content from decrypted file
try (ReadableFile readable = cleartextFs.file("test1.txt").openReadable()) {
ByteBuffer buf = ByteBuffer.allocate(11);
readable.read(buf);
buf.flip();
Assert.assertArrayEquals("Hello World".getBytes(), buf.array());
}
}
@Test(timeout = 20000) // assuming a minimum speed of 10mb/s during encryption and decryption 20s should be enough
public void testEncryptionAndDecryptionSpeed() throws InterruptedException, IOException {
File file = cleartextFs.file("benchmark.test");
final long encStart = System.nanoTime();
try (WritableFile writable = file.openWritable()) {
final ByteBuffer cleartext = ByteBuffer.allocate(100000); // 100k
for (int i = 0; i < 1000; i++) { // 100M total
cleartext.rewind();
writable.write(cleartext);
}
}
final long encEnd = System.nanoTime();
LOG.debug("Encryption of 100M took {}ms", (encEnd - encStart) / 1000 / 1000);
final long decStart = System.nanoTime();
try (ReadableFile readable = file.openReadable()) {
final ByteBuffer cleartext = ByteBuffer.allocate(100000); // 100k
for (int i = 0; i < 1000; i++) { // 100M total
cleartext.clear();
readable.read(cleartext);
cleartext.flip();
Assert.assertEquals(cleartext.get(), 0x00);
}
}
final long decEnd = System.nanoTime();
LOG.debug("Decryption of 100M took {}ms", (decEnd - decStart) / 1000 / 1000);
file.delete();
}
@Test
public void testRandomAccessOnLastBlock() {
// prepare test data:
ByteBuffer testData = ByteBuffer.allocate(16000 * Integer.BYTES); // < 64kb
for (int i = 0; i < 16000; i++) {
testData.putInt(i);
}
// write test data to file:
File cleartextFile = cleartextFs.file("test");
try (WritableFile writable = cleartextFile.openWritable()) {
testData.flip();
writable.write(testData);
}
// read last block:
try (ReadableFile readable = cleartextFile.openReadable()) {
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
buf.clear();
readable.position(15999 * Integer.BYTES);
readable.read(buf);
buf.flip();
Assert.assertEquals(15999, buf.getInt());
}
}
@Test
public void testSequentialRandomAccess() {
// prepare test data:
ByteBuffer testData = ByteBuffer.allocate(1_000_000 * Integer.BYTES); // = 4MB
for (int i = 0; i < 1000000; i++) {
testData.putInt(i);
}
// write test data to file:
File cleartextFile = cleartextFs.file("test");
try (WritableFile writable = cleartextFile.openWritable()) {
testData.flip();
writable.write(testData);
}
// shuffle our test positions:
List<Integer> nums = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
nums.add(i);
}
Collections.shuffle(nums);
// read parts from positions:
try (ReadableFile readable = cleartextFile.openReadable()) {
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
for (int i = 0; i < 1000; i++) {
int num = nums.get(i);
buf.clear();
readable.position(num * Integer.BYTES);
readable.read(buf);
buf.flip();
Assert.assertEquals(num, buf.getInt());
}
}
}
@Test
public void testParallelRandomAccess() {
// prepare test data:
ByteBuffer testData = ByteBuffer.allocate(1_000_000 * Integer.BYTES); // = 4MB
for (int i = 0; i < 1000000; i++) {
testData.putInt(i);
}
// write test data to file:
final File cleartextFile = cleartextFs.file("test");
try (WritableFile writable = cleartextFile.openWritable()) {
testData.flip();
writable.write(testData);
}
// shuffle our test positions:
List<Integer> nums = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
nums.add(i);
}
Collections.shuffle(nums);
// read parts from positions in parallel:
final ForkJoinPool pool = new ForkJoinPool(10);
final List<Future<Boolean>> tasks = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
final int num = nums.get(i);
final ForkJoinTask<Boolean> task = ForkJoinTask.adapt(() -> {
try (ReadableFile readable = cleartextFile.openReadable()) {
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
buf.clear();
readable.position(num * Integer.BYTES);
readable.read(buf);
buf.flip();
int numRead = buf.getInt();
return num == numRead;
}
});
pool.execute(task);
tasks.add(task);
}
// Wait for tasks to finish and check results
Assert.assertTrue(tasks.stream().allMatch(task -> {
try {
return task.get();
} catch (Exception e) {
e.printStackTrace();
return false;
}
}));
}
}

View File

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

View File

@@ -12,14 +12,14 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.0.3</version>
<version>1.2.4</version>
</parent>
<artifactId>filesystem-crypto</artifactId>
<name>Cryptomator filesystem: Encryption layer</name>
<properties>
<bouncycastle.version>1.51</bouncycastle.version>
<sivmode.version>1.0.2</sivmode.version>
<sivmode.version>1.2.0</sivmode.version>
</properties>
<dependencies>

View File

@@ -1,29 +0,0 @@
/*******************************************************************************
* 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.crypto.engine;
public class AuthenticationFailedException extends CryptoException {
public AuthenticationFailedException() {
super();
}
public AuthenticationFailedException(String message) {
super(message);
}
public AuthenticationFailedException(Throwable cause) {
super(cause);
}
public AuthenticationFailedException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,29 +0,0 @@
/*******************************************************************************
* 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.crypto.engine;
abstract class CryptoException extends RuntimeException {
public CryptoException() {
super();
}
public CryptoException(String message) {
super(message);
}
public CryptoException(Throwable cause) {
super(cause);
}
public CryptoException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,31 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 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.crypto.engine;
import javax.security.auth.Destroyable;
/**
* A Cryptor instance, once initialized with a set of keys, provides access to threadsafe cryptographic routines.
*/
public interface Cryptor extends Destroyable {
FilenameCryptor getFilenameCryptor();
FileContentCryptor getFileContentCryptor();
void randomizeMasterkey();
void readKeysFromMasterkeyFile(byte[] masterkeyFileContents, CharSequence passphrase) throws InvalidPassphraseException, UnsupportedVaultFormatException;
byte[] writeKeysToMasterkeyFile(CharSequence passphrase);
@Override
void destroy();
}

View File

@@ -1,49 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 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.crypto.engine;
import java.nio.ByteBuffer;
import java.util.Optional;
/**
* Factory for stateful {@link FileContentEncryptor Encryptor}/{@link FileContentDecryptor Decryptor} instances, that are capable of processing data exactly once.
*/
public interface FileContentCryptor {
public static final ByteBuffer EOF = ByteBuffer.allocate(0);
/**
* @return The fixed number of bytes of the file header. The header length is implementation-specific.
*/
int getHeaderSize();
/**
* @return The ciphertext position that correlates to the cleartext position.
*/
long toCiphertextPos(long cleartextPos);
/**
* @param header The full fixed-length header of an encrypted file. The caller is required to pass the exact amount of bytes returned by {@link #getHeaderSize()}.
* @param firstCiphertextByte Position of the first ciphertext byte passed to the decryptor. If the decryptor can not fast-forward to the requested byte, an exception is thrown.
* If firstCiphertextByte is an invalid starting point, i.e. doesn't align with the decryptors internal block size, an IllegalArgumentException will be thrown.
* @param authenticate Skip authentication by setting this flag to <code>false</code>. Should be <code>true</code> by default.
* @return A possibly new FileContentDecryptor instance which is capable of decrypting ciphertexts associated with the given file header.
*/
FileContentDecryptor createFileContentDecryptor(ByteBuffer header, long firstCiphertextByte, boolean authenticate) throws IllegalArgumentException, AuthenticationFailedException;
/**
* @param header The full fixed-length header of an encrypted file or {@link Optional#empty()}. The caller is required to pass the exact amount of bytes returned by {@link #getHeaderSize()}.
* If the header is empty, a new one will be created by the returned encryptor.
* @param firstCleartextByte Position of the first cleartext byte passed to the encryptor. If the encryptor can not fast-forward to the requested byte, an exception is thrown.
* If firstCiphertextByte is an invalid starting point, i.e. doesn't align with the encryptors internal block size, an IllegalArgumentException will be thrown.
* @return A possibly new FileContentEncryptor instance which is capable of encrypting cleartext associated with the given file header.
*/
FileContentEncryptor createFileContentEncryptor(Optional<ByteBuffer> header, long firstCleartextByte) throws IllegalArgumentException;
}

View File

@@ -1,66 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 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.crypto.engine;
import java.io.Closeable;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import javax.security.auth.Destroyable;
/**
* Stateful, thus not thread-safe.
*/
public interface FileContentDecryptor extends Destroyable, Closeable {
/**
* @return Number of bytes of the decrypted file.
*/
long contentLength();
/**
* Appends further ciphertext to this decryptor. This method might block until space becomes available. If so, it is interruptable.
*
* @param cleartext Cleartext data or {@link FileContentCryptor#EOF} to indicate the end of a ciphertext.
* @see #skipToPosition(long)
*/
void append(ByteBuffer ciphertext) throws InterruptedException;
/**
* Cancels decryption due to an exception in the thread responsible for appending ciphertext.
* The exception will be the root cause of an {@link UncheckedIOException} thrown by {@link #cleartext()} when retrieving the decrypted result.
*
* @param cause The exception making it impossible to {@link #append(ByteBuffer)} further ciphertext.
*/
void cancelWithException(Exception cause) throws InterruptedException;
/**
* Returns the next decrypted cleartext in byte-by-byte FIFO order, meaning in the order ciphertext has been appended to this encryptor.
* However the number and size of the cleartext byte buffers doesn't need to resemble the ciphertext buffers.
*
* This method might block if no cleartext is available yet.
*
* @return Decrypted cleartext or {@link FileContentCryptor#EOF}.
* @throws AuthenticationFailedException On MAC mismatches
* @throws UncheckedIOException In case of I/O exceptions, e.g. caused by previous {@link #cancelWithException(Exception)}.
*/
ByteBuffer cleartext() throws InterruptedException, AuthenticationFailedException, UncheckedIOException;
/**
* Clears file-specific sensitive information.
*/
@Override
void destroy();
@Override
default void close() {
this.destroy();
}
}

View File

@@ -1,72 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 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.crypto.engine;
import java.io.Closeable;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import javax.security.auth.Destroyable;
/**
* Stateful, thus not thread-safe.
*/
public interface FileContentEncryptor extends Destroyable, Closeable {
/**
* Creates the encrypted file header. This header might depend on the already encrypted data,
* thus the caller should make sure all data is processed before requesting the header.
*
* @return Encrypted file header.
*/
ByteBuffer getHeader();
/**
* @return the size of headers created by this {@code FileContentCryptor}. The length of headers returned by {@link #getHeader()} equals this value.
*/
int getHeaderSize();
/**
* Appends further cleartext to this encryptor. This method might block until space becomes available.
*
* @param cleartext Cleartext data or {@link FileContentCryptor#EOF} to indicate the end of a cleartext.
*/
void append(ByteBuffer cleartext) throws InterruptedException;
/**
* Cancels encryption due to an exception in the thread responsible for appending cleartext.
* The exception will be the root cause of an {@link UncheckedIOException} thrown by {@link #ciphertext()} when retrieving the encrypted result.
*
* @param cause The exception making it impossible to {@link #append(ByteBuffer)} further cleartext.
*/
void cancelWithException(Exception cause) throws InterruptedException;
/**
* Returns the next ciphertext in byte-by-byte FIFO order, meaning in the order cleartext has been appended to this encryptor.
* However the number and size of the ciphertext byte buffers doesn't need to resemble the cleartext buffers.
*
* This method might block if no ciphertext is available yet.
*
* @return Encrypted ciphertext of {@link FileContentCryptor#EOF}.
* @throws UncheckedIOException In case of I/O exceptions, e.g. caused by previous {@link #cancelWithException(Exception)}.
*/
ByteBuffer ciphertext() throws InterruptedException, UncheckedIOException;
/**
* Clears file-specific sensitive information.
*/
@Override
void destroy();
@Override
default void close() {
this.destroy();
}
}

View File

@@ -1,45 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 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.crypto.engine;
/**
* Provides deterministic encryption capabilities as filenames must not change on subsequent encryption attempts,
* otherwise each change results in major directory structure changes which would be a terrible idea for cloud storage encryption.
*
* @see <a href="https://en.wikipedia.org/wiki/Deterministic_encryption">Wikipedia on deterministic encryption</a>
*/
public interface FilenameCryptor {
/**
* @return constant length string, that is unlikely to collide with any other name.
*/
String hashDirectoryId(String cleartextDirectoryId);
/**
* Tests without an actual decryption attempt, if a name is a well-formed ciphertext.
*
* @param ciphertextName Filename in question
* @return <code>true</code> if the given name is likely to be a valid ciphertext
*/
boolean isEncryptedFilename(String ciphertextName);
/**
* @param cleartextName original filename including cleartext file extension
* @param associatedData optional associated data, that will not get encrypted but needs to be provided during decryption
* @return encrypted filename without any file extension
*/
String encryptFilename(String cleartextName, byte[]... associatedData);
/**
* @param ciphertextName Ciphertext only, with any additional strings like file extensions stripped first.
* @param associatedData the same associated data used during encryption, otherwise and {@link AuthenticationFailedException} will be thrown
* @return cleartext filename, probably including its cleartext file extension.
*/
String decryptFilename(String ciphertextName, byte[]... associatedData) throws AuthenticationFailedException;
}

View File

@@ -1,17 +0,0 @@
/*******************************************************************************
* 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.crypto.engine;
public class InvalidPassphraseException extends CryptoException {
public InvalidPassphraseException() {
super();
}
}

View File

@@ -1,38 +0,0 @@
/*******************************************************************************
* 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.crypto.engine;
public class UnsupportedVaultFormatException extends CryptoException {
private final Integer detectedVersion;
private final Integer supportedVersion;
public UnsupportedVaultFormatException(Integer detectedVersion, Integer supportedVersion) {
super("Tried to open vault of version " + detectedVersion + ", but can only handle version " + supportedVersion);
this.detectedVersion = detectedVersion;
this.supportedVersion = supportedVersion;
}
public Integer getDetectedVersion() {
return detectedVersion;
}
public Integer getSupportedVersion() {
return supportedVersion;
}
public boolean isVaultOlderThanSoftware() {
return detectedVersion == null || detectedVersion < supportedVersion;
}
public boolean isSoftwareOlderThanVault() {
return detectedVersion > supportedVersion;
}
}

View File

@@ -1,71 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 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.crypto.engine.impl;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
final class AesKeyWrap {
private static final String RFC3394_CIPHER = "AESWrap";
private AesKeyWrap() {
}
/**
* @param kek Key encrypting key
* @param key Key to be wrapped
* @return Wrapped key
*/
public static byte[] wrap(SecretKey kek, SecretKey key) {
final Cipher cipher;
try {
cipher = Cipher.getInstance(RFC3394_CIPHER);
cipher.init(Cipher.WRAP_MODE, kek);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException("Invalid key.", e);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException("Algorithm/Padding should exist.", e);
}
try {
return cipher.wrap(key);
} catch (InvalidKeyException | IllegalBlockSizeException e) {
throw new IllegalStateException("Unable to wrap key.", e);
}
}
/**
* @param kek Key encrypting key
* @param wrappedKey Key to be unwrapped
* @param keyAlgorithm Key designation, i.e. algorithm name to be associated with the unwrapped key.
* @return Unwrapped key
* @throws NoSuchAlgorithmException If keyAlgorithm is unknown
* @throws InvalidKeyException If unwrapping failed (i.e. wrong kek)
*/
public static SecretKey unwrap(SecretKey kek, byte[] wrappedKey, String keyAlgorithm) throws InvalidKeyException, NoSuchAlgorithmException {
final Cipher cipher;
try {
cipher = Cipher.getInstance(RFC3394_CIPHER);
cipher.init(Cipher.UNWRAP_MODE, kek);
} catch (InvalidKeyException ex) {
throw new IllegalArgumentException("Invalid key.", ex);
} catch (NoSuchAlgorithmException | NoSuchPaddingException ex) {
throw new IllegalStateException("Algorithm/Padding should exist.", ex);
}
return (SecretKey) cipher.unwrap(wrappedKey, keyAlgorithm, Cipher.SECRET_KEY);
}
}

View File

@@ -1,15 +0,0 @@
package org.cryptomator.crypto.engine.impl;
public final class Constants {
private Constants() {
}
static final Integer CURRENT_VAULT_VERSION = 3;
public static final int PAYLOAD_SIZE = 32 * 1024;
public static final int NONCE_SIZE = 16;
public static final int MAC_SIZE = 32;
public static final int CHUNK_SIZE = NONCE_SIZE + PAYLOAD_SIZE + MAC_SIZE;
}

View File

@@ -1,41 +0,0 @@
/*******************************************************************************
* 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.crypto.engine.impl;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import org.cryptomator.crypto.engine.Cryptor;
import dagger.Module;
import dagger.Provides;
@Module
public class CryptoEngineModule {
@Provides
public Cryptor provideCryptor(SecureRandom secureRandom) {
return new CryptorImpl(secureRandom);
}
@Provides
public SecureRandom provideSecureRandom() {
try {
// 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

@@ -1,195 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 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.crypto.engine.impl;
import static org.cryptomator.crypto.engine.impl.Constants.CURRENT_VAULT_VERSION;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.DestroyFailedException;
import javax.security.auth.Destroyable;
import org.cryptomator.common.LazyInitializer;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.crypto.engine.FileContentCryptor;
import org.cryptomator.crypto.engine.FilenameCryptor;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
class CryptorImpl implements Cryptor {
private static final int SCRYPT_SALT_LENGTH = 8;
private static final int SCRYPT_COST_PARAM = 1 << 14;
private static final int SCRYPT_BLOCK_SIZE = 8;
private static final int KEYLENGTH_IN_BYTES = 32;
private static final String ENCRYPTION_ALG = "AES";
private static final String MAC_ALG = "HmacSHA256";
private SecretKey encryptionKey;
private SecretKey macKey;
private final AtomicReference<FilenameCryptor> filenameCryptor = new AtomicReference<>();
private final AtomicReference<FileContentCryptor> fileContentCryptor = new AtomicReference<>();
private final SecureRandom randomSource;
public CryptorImpl(SecureRandom randomSource) {
this.randomSource = randomSource;
}
@Override
public FilenameCryptor getFilenameCryptor() {
assertKeysExist();
return LazyInitializer.initializeLazily(filenameCryptor, () -> {
return new FilenameCryptorImpl(encryptionKey, macKey);
});
}
@Override
public FileContentCryptor getFileContentCryptor() {
assertKeysExist();
return LazyInitializer.initializeLazily(fileContentCryptor, () -> {
return new FileContentCryptorImpl(encryptionKey, macKey, randomSource);
});
}
private void assertKeysExist() {
if (encryptionKey == null || encryptionKey.isDestroyed()) {
throw new IllegalStateException("No or invalid encryptionKey.");
}
if (macKey == null || macKey.isDestroyed()) {
throw new IllegalStateException("No or invalid MAC key.");
}
}
@Override
public void randomizeMasterkey() {
final byte[] randomBytes = new byte[KEYLENGTH_IN_BYTES];
try {
randomSource.nextBytes(randomBytes);
encryptionKey = new SecretKeySpec(randomBytes, ENCRYPTION_ALG);
randomSource.nextBytes(randomBytes);
macKey = new SecretKeySpec(randomBytes, ENCRYPTION_ALG);
} finally {
Arrays.fill(randomBytes, (byte) 0x00);
}
}
@Override
public void readKeysFromMasterkeyFile(byte[] masterkeyFileContents, CharSequence passphrase) {
final KeyFile keyFile;
try {
final ObjectMapper om = new ObjectMapper();
keyFile = om.readValue(masterkeyFileContents, KeyFile.class);
if (keyFile == null) {
throw new InvalidFormatException("Could not read masterkey file", null, KeyFile.class);
}
} catch (IOException e) {
throw new IllegalArgumentException("Unable to parse masterkeyFileContents", e);
}
assert keyFile != null;
// check version
if (!CURRENT_VAULT_VERSION.equals(keyFile.getVersion())) {
throw new UnsupportedVaultFormatException(keyFile.getVersion(), CURRENT_VAULT_VERSION);
}
final byte[] kekBytes = Scrypt.scrypt(passphrase, keyFile.getScryptSalt(), keyFile.getScryptCostParam(), keyFile.getScryptBlockSize(), KEYLENGTH_IN_BYTES);
try {
final SecretKey kek = new SecretKeySpec(kekBytes, ENCRYPTION_ALG);
this.macKey = AesKeyWrap.unwrap(kek, keyFile.getMacMasterKey(), MAC_ALG);
// future use (as soon as we need to prevent downgrade attacks):
// final Mac mac = new ThreadLocalMac(macKey, MAC_ALG).get();
// final byte[] versionMac = mac.doFinal(ByteBuffer.allocate(Integer.BYTES).putInt(CURRENT_VAULT_VERSION).array());
// if (!MessageDigest.isEqual(versionMac, keyFile.getVersionMac())) {
// destroyQuietly(macKey);
// throw new UnsupportedVaultFormatException(Integer.MAX_VALUE, CURRENT_VAULT_VERSION);
// }
this.encryptionKey = AesKeyWrap.unwrap(kek, keyFile.getEncryptionMasterKey(), ENCRYPTION_ALG);
} catch (InvalidKeyException e) {
throw new InvalidPassphraseException();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Hard-coded algorithm doesn't exist.", e);
} finally {
Arrays.fill(kekBytes, (byte) 0x00);
}
}
@Override
public byte[] writeKeysToMasterkeyFile(CharSequence passphrase) {
final byte[] scryptSalt = new byte[SCRYPT_SALT_LENGTH];
randomSource.nextBytes(scryptSalt);
final byte[] kekBytes = Scrypt.scrypt(passphrase, scryptSalt, SCRYPT_COST_PARAM, SCRYPT_BLOCK_SIZE, KEYLENGTH_IN_BYTES);
final byte[] wrappedEncryptionKey;
final byte[] wrappedMacKey;
try {
final SecretKey kek = new SecretKeySpec(kekBytes, ENCRYPTION_ALG);
wrappedEncryptionKey = AesKeyWrap.wrap(kek, encryptionKey);
wrappedMacKey = AesKeyWrap.wrap(kek, macKey);
} finally {
Arrays.fill(kekBytes, (byte) 0x00);
}
final Mac mac = new ThreadLocalMac(macKey, MAC_ALG).get();
final byte[] versionMac = mac.doFinal(ByteBuffer.allocate(Integer.BYTES).putInt(CURRENT_VAULT_VERSION).array());
final KeyFile keyfile = new KeyFile();
keyfile.setVersion(CURRENT_VAULT_VERSION);
keyfile.setScryptSalt(scryptSalt);
keyfile.setScryptCostParam(SCRYPT_COST_PARAM);
keyfile.setScryptBlockSize(SCRYPT_BLOCK_SIZE);
keyfile.setEncryptionMasterKey(wrappedEncryptionKey);
keyfile.setMacMasterKey(wrappedMacKey);
keyfile.setVersionMac(versionMac);
try {
final ObjectMapper om = new ObjectMapper();
return om.writeValueAsBytes(keyfile);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Unable to create JSON from " + keyfile, e);
}
}
/* ======================= destruction ======================= */
@Override
public void destroy() {
destroyQuietly(encryptionKey);
destroyQuietly(macKey);
}
@Override
public boolean isDestroyed() {
return (encryptionKey == null || encryptionKey.isDestroyed()) && (macKey == null || macKey.isDestroyed());
}
private void destroyQuietly(Destroyable d) {
if (d == null) {
return;
}
try {
d.destroy();
} catch (DestroyFailedException e) {
// ignore
}
}
}

View File

@@ -1,70 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 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.crypto.engine.impl;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
/**
* Executes long-running computations and returns the result strictly in order of the job submissions, no matter how long each job takes.
*
* The internally used thread pool is shut down automatically as soon as this FifiParallelDataProcessor is no longer referenced (see Finalization behaviour of {@link ThreadPoolExecutor}).
*/
class FifoParallelDataProcessor<T> {
private final BlockingQueue<Future<T>> processedData;
private final ExecutorService executorService;
/**
* @param numThreads How many jobs can run in parallel.
* @param workAhead Maximum number of jobs accepted in {@link #submit(Callable)} without blocking until results are polled from {@link #processedData()}.
*/
public FifoParallelDataProcessor(int workAhead, ExecutorService executorService) {
this.processedData = new ArrayBlockingQueue<>(workAhead);
this.executorService = executorService;
}
/**
* Enqueues a job for execution. The results of multiple submissions can be polled in FIFO order using {@link #processedData()}.
*
* @param processingJob A task, that will compute a result.
* @throws InterruptedException
*/
void submit(Callable<T> processingJob) throws InterruptedException {
Future<T> future = executorService.submit(processingJob);
processedData.put(future);
}
/**
* Submits already pre-processed data, that can be polled in FIFO order from {@link #processedData()}.
*
* @throws InterruptedException
*/
void submitPreprocessed(T preprocessedData) throws InterruptedException {
this.submit(() -> {
return preprocessedData;
});
}
/**
* Result of previously {@link #submit(Callable) submitted} jobs in the same order as they have been submitted. Blocks if the job didn't finish yet.
*
* @return Next job result
* @throws InterruptedException If the calling thread was interrupted while waiting for the next result.
*/
T processedData() throws InterruptedException, ExecutionException {
return processedData.take().get();
}
}

Some files were not shown because too many files have changed in this diff Show More