Create new project code style config and reformat code base with it

This commit is contained in:
Julian Raufelder 2021-02-18 16:13:21 +01:00
parent 7263aff180
commit 667fb82165
No known key found for this signature in database
GPG Key ID: 17EE71F6634E381D
436 changed files with 3234 additions and 3059 deletions

13
.gitignore vendored
View File

@ -3,7 +3,18 @@
*.iml *.iml
*.ipr *.ipr
*.iws *.iws
.idea/ .idea/**/workspace.xml
.idea/**/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/**/libraries/
.idea/caches
.idea/modules.xml
.idea/navEditor.xml
.idea/encodings.xml
.idea/compiler.xml
.idea/jarRepositories.xml
###Android### ###Android###

187
.idea/codeStyles/Project.xml generated Normal file
View File

@ -0,0 +1,187 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="AUTODETECT_INDENTS" value="false" />
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="USE_TAB_CHARACTER" value="true" />
</value>
</option>
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="RIGHT_MARGIN" value="220" />
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="android" alias="false" withSubpackages="true" />
<package name="androidx" alias="false" withSubpackages="true" />
<package name="com" alias="false" withSubpackages="true" />
<package name="junit" alias="false" withSubpackages="true" />
<package name="net" alias="false" withSubpackages="true" />
<package name="org" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="" alias="false" withSubpackages="true" />
<package name="" alias="false" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="999" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="999" />
</JetCodeStyleSettings>
<Properties>
<option name="KEEP_BLANK_LINES" value="true" />
</Properties>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="Groovy">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="HTML">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="KEEP_LINE_BREAKS" value="false" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JSON">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

6
.idea/codeStyles/codeStyleConfig.xml generated Executable file
View File

@ -0,0 +1,6 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Project" />
</state>
</component>

View File

@ -0,0 +1,13 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ConstantConditions" enabled="true" level="WARNING" enabled_by_default="true">
<option name="SUGGEST_NULLABLE_ANNOTATIONS" value="false" />
<option name="DONT_REPORT_TRUE_ASSERT_STATEMENTS" value="false" />
</inspection_tool>
<inspection_tool class="SerializableHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreAnonymousInnerClasses" value="false" />
<option name="superClassString" value="java.awt.Component" />
</inspection_tool>
</profile>
</component>

6
.idea/kotlinc.xml generated Executable file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="12" />
</component>
</project>

54
.idea/misc.xml generated Executable file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="org.jetbrains.annotations.NotNull" />
<option name="myNullables">
<value>
<list size="15">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
<item index="10" class="java.lang.String" itemvalue="android.annotation.Nullable" />
<item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
<item index="12" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.annotations.Nullable" />
<item index="14" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="14">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
<item index="9" class="java.lang.String" itemvalue="android.annotation.NonNull" />
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
<item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
<item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

12
.idea/runConfigurations.xml generated Executable file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

8
.idea/vcs.xml generated Executable file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/msa-auth-for-android" vcs="Git" />
<mapping directory="$PROJECT_DIR$/subsampling-scale-image-view" vcs="Git" />
</component>
</project>

View File

@ -16,8 +16,7 @@ import java.io.File;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
public abstract class InterceptingCloudContentRepository<CloudType extends Cloud, NodeType extends CloudNode, DirType extends CloudFolder, FileType extends CloudFile> public abstract class InterceptingCloudContentRepository<CloudType extends Cloud, NodeType extends CloudNode, DirType extends CloudFolder, FileType extends CloudFile> implements CloudContentRepository<CloudType, NodeType, DirType, FileType> {
implements CloudContentRepository<CloudType, NodeType, DirType, FileType> {
private final CloudContentRepository<CloudType, NodeType, DirType, FileType> delegate; private final CloudContentRepository<CloudType, NodeType, DirType, FileType> delegate;

View File

@ -52,10 +52,12 @@ public class CryptoCloud implements Cloud {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) if (obj == null || getClass() != obj.getClass()) {
return false; return false;
if (obj == this) }
if (obj == this) {
return true; return true;
}
return internalEquals((CryptoCloud) obj); return internalEquals((CryptoCloud) obj);
} }

View File

@ -1,10 +1,6 @@
package org.cryptomator.data.cloud.crypto; package org.cryptomator.data.cloud.crypto;
import static java.lang.String.format; import android.content.Context;
import java.io.File;
import java.io.OutputStream;
import java.util.List;
import org.cryptomator.cryptolib.api.Cryptor; import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.domain.CloudFolder; import org.cryptomator.domain.CloudFolder;
@ -19,7 +15,11 @@ import org.cryptomator.domain.usecases.cloud.UploadState;
import org.cryptomator.util.Optional; import org.cryptomator.util.Optional;
import org.cryptomator.util.Supplier; import org.cryptomator.util.Supplier;
import android.content.Context; import java.io.File;
import java.io.OutputStream;
import java.util.List;
import static java.lang.String.format;
class CryptoCloudContentRepository implements CloudContentRepository<CryptoCloud, CryptoNode, CryptoFolder, CryptoFile> { class CryptoCloudContentRepository implements CloudContentRepository<CryptoCloud, CryptoNode, CryptoFolder, CryptoFile> {

View File

@ -1,23 +1,5 @@
package org.cryptomator.data.cloud.crypto; package org.cryptomator.data.cloud.crypto;
import static android.R.attr.version;
import static java.text.Normalizer.normalize;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.DATA_DIR_NAME;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.MASTERKEY_BACKUP_FILE_EXT;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.MASTERKEY_FILE_NAME;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.MAX_VAULT_VERSION;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.MIN_VAULT_VERSION;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.ROOT_DIR_ID;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.VERSION_WITH_NORMALIZED_PASSWORDS;
import static org.cryptomator.domain.Vault.aCopyOf;
import static org.cryptomator.domain.usecases.ProgressAware.NO_OP_PROGRESS_AWARE;
import java.io.ByteArrayOutputStream;
import java.text.Normalizer;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.cryptomator.cryptolib.Cryptors; import org.cryptomator.cryptolib.Cryptors;
import org.cryptomator.cryptolib.api.Cryptor; import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.CryptorProvider; import org.cryptomator.cryptolib.api.CryptorProvider;
@ -36,6 +18,24 @@ import org.cryptomator.domain.usecases.cloud.Flag;
import org.cryptomator.domain.usecases.vault.UnlockToken; import org.cryptomator.domain.usecases.vault.UnlockToken;
import org.cryptomator.util.Optional; import org.cryptomator.util.Optional;
import java.io.ByteArrayOutputStream;
import java.text.Normalizer;
import javax.inject.Inject;
import javax.inject.Singleton;
import static android.R.attr.version;
import static java.text.Normalizer.normalize;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.DATA_DIR_NAME;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.MASTERKEY_BACKUP_FILE_EXT;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.MASTERKEY_FILE_NAME;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.MAX_VAULT_VERSION;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.MIN_VAULT_VERSION;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.ROOT_DIR_ID;
import static org.cryptomator.data.cloud.crypto.CryptoConstants.VERSION_WITH_NORMALIZED_PASSWORDS;
import static org.cryptomator.domain.Vault.aCopyOf;
import static org.cryptomator.domain.usecases.ProgressAware.NO_OP_PROGRESS_AWARE;
@Singleton @Singleton
public class CryptoCloudFactory { public class CryptoCloudFactory {
@ -171,26 +171,6 @@ public class CryptoCloudFactory {
createNewMasterKeyFile(data, vaultVersion, oldPassword, newPassword, vaultLocation); createNewMasterKeyFile(data, vaultVersion, oldPassword, newPassword, vaultLocation);
} }
private static class UnlockTokenImpl implements UnlockToken {
private final Vault vault;
private final byte[] keyFileData;
private UnlockTokenImpl(Vault vault, byte[] keyFileData) {
this.vault = vault;
this.keyFileData = keyFileData;
}
@Override
public Vault getVault() {
return vault;
}
public KeyFile getKeyFile() {
return KeyFile.parse(keyFileData);
}
}
private void createBackupMasterKeyFile(byte[] data, CloudFolder vaultLocation) throws BackendException { private void createBackupMasterKeyFile(byte[] data, CloudFolder vaultLocation) throws BackendException {
cloudContentRepository.write( // cloudContentRepository.write( //
masterkeyBackupFile(vaultLocation, data), // masterkeyBackupFile(vaultLocation, data), //
@ -220,4 +200,24 @@ public class CryptoCloudFactory {
} }
} }
private static class UnlockTokenImpl implements UnlockToken {
private final Vault vault;
private final byte[] keyFileData;
private UnlockTokenImpl(Vault vault, byte[] keyFileData) {
this.vault = vault;
this.keyFileData = keyFileData;
}
@Override
public Vault getVault() {
return vault;
}
public KeyFile getKeyFile() {
return KeyFile.parse(keyFileData);
}
}
} }

View File

@ -61,10 +61,12 @@ class CryptoFile implements CloudFile, CryptoNode {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) if (obj == null || getClass() != obj.getClass()) {
return false; return false;
if (obj == this) }
if (obj == this) {
return true; return true;
}
return internalEquals((CryptoFile) obj); return internalEquals((CryptoFile) obj);
} }

View File

@ -47,10 +47,12 @@ class CryptoFolder implements CloudFolder, CryptoNode {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) if (obj == null || getClass() != obj.getClass()) {
return false; return false;
if (obj == this) }
if (obj == this) {
return true; return true;
}
return internalEquals((CryptoFolder) obj); return internalEquals((CryptoFolder) obj);
} }

View File

@ -243,8 +243,7 @@ abstract class CryptoImplDecorator {
} }
} }
private CryptoFile writeFromTmpFile(DataSource originalDataSource, final CryptoFile cryptoFile, File encryptedFile, final ProgressAware<UploadState> progressAware, boolean replace) private CryptoFile writeFromTmpFile(DataSource originalDataSource, final CryptoFile cryptoFile, File encryptedFile, final ProgressAware<UploadState> progressAware, boolean replace) throws BackendException, IOException {
throws BackendException, IOException {
CryptoFile targetFile = targetFile(cryptoFile, replace); CryptoFile targetFile = targetFile(cryptoFile, replace);
return file(targetFile, // return file(targetFile, //
cloudContentRepository.write( // cloudContentRepository.write( //
@ -298,7 +297,7 @@ abstract class CryptoImplDecorator {
try { try {
File encryptedTmpFile = readToTmpFile(cryptoFile, ciphertextFile, progressAware); File encryptedTmpFile = readToTmpFile(cryptoFile, ciphertextFile, progressAware);
progressAware.onProgress(Progress.started(DownloadState.decryption(cryptoFile))); progressAware.onProgress(Progress.started(DownloadState.decryption(cryptoFile)));
try (ReadableByteChannel readableByteChannel = Channels.newChannel(new FileInputStream(encryptedTmpFile)); try (ReadableByteChannel readableByteChannel = Channels.newChannel(new FileInputStream(encryptedTmpFile)); //
ReadableByteChannel decryptingReadableByteChannel = new DecryptingReadableByteChannel(readableByteChannel, cryptor(), true)) { ReadableByteChannel decryptingReadableByteChannel = new DecryptingReadableByteChannel(readableByteChannel, cryptor(), true)) {
ByteBuffer buff = ByteBuffer.allocate(cryptor().fileContentCryptor().ciphertextChunkSize()); ByteBuffer buff = ByteBuffer.allocate(cryptor().fileContentCryptor().ciphertextChunkSize());
long cleartextSize = cryptoFile.getSize().orElse(Long.MAX_VALUE); long cleartextSize = cryptoFile.getSize().orElse(Long.MAX_VALUE);
@ -395,7 +394,7 @@ abstract class CryptoImplDecorator {
} }
try (InputStream stream = data.open(context)) { try (InputStream stream = data.open(context)) {
File encryptedTmpFile = File.createTempFile(UUID.randomUUID().toString(), ".crypto", getInternalCache()); File encryptedTmpFile = File.createTempFile(UUID.randomUUID().toString(), ".crypto", getInternalCache());
try (WritableByteChannel writableByteChannel = Channels.newChannel(new FileOutputStream(encryptedTmpFile)); try (WritableByteChannel writableByteChannel = Channels.newChannel(new FileOutputStream(encryptedTmpFile)); //
WritableByteChannel encryptingWritableByteChannel = new EncryptingWritableByteChannel(writableByteChannel, cryptor())) { WritableByteChannel encryptingWritableByteChannel = new EncryptingWritableByteChannel(writableByteChannel, cryptor())) {
progressAware.onProgress(Progress.started(UploadState.encryption(cryptoFile))); progressAware.onProgress(Progress.started(UploadState.encryption(cryptoFile)));
ByteBuffer buff = ByteBuffer.allocate(cryptor().fileContentCryptor().cleartextChunkSize()); ByteBuffer buff = ByteBuffer.allocate(cryptor().fileContentCryptor().cleartextChunkSize());

View File

@ -3,4 +3,5 @@ package org.cryptomator.data.cloud.crypto;
import org.cryptomator.domain.CloudNode; import org.cryptomator.domain.CloudNode;
interface CryptoNode extends CloudNode { interface CryptoNode extends CloudNode {
} }

View File

@ -61,10 +61,12 @@ class CryptoSymlink implements CloudFile, CryptoNode {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) if (obj == null || getClass() != obj.getClass()) {
return false; return false;
if (obj == this) }
if (obj == this) {
return true; return true;
}
return internalEquals((CryptoSymlink) obj); return internalEquals((CryptoSymlink) obj);
} }

View File

@ -1,16 +1,16 @@
package org.cryptomator.data.cloud.crypto; package org.cryptomator.data.cloud.crypto;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.cryptomator.cryptolib.api.Cryptor; import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.domain.Vault; import org.cryptomator.domain.Vault;
import org.cryptomator.domain.exception.MissingCryptorException; import org.cryptomator.domain.exception.MissingCryptorException;
import org.cryptomator.util.Optional; import org.cryptomator.util.Optional;
import org.cryptomator.util.Supplier; import org.cryptomator.util.Supplier;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public abstract class Cryptors { public abstract class Cryptors {
Cryptors() { Cryptors() {

View File

@ -1,9 +1,9 @@
package org.cryptomator.data.cloud.crypto; package org.cryptomator.data.cloud.crypto;
import java.util.Map;
import android.util.LruCache; import android.util.LruCache;
import java.util.Map;
class DirIdCacheFormat7 implements DirIdCache { class DirIdCacheFormat7 implements DirIdCache {
private static final int MAX_SIZE = 1024; private static final int MAX_SIZE = 1024;
@ -43,22 +43,24 @@ class DirIdCacheFormat7 implements DirIdCache {
private static class DirIdCacheKey { private static class DirIdCacheKey {
static DirIdCacheKey toKey(CryptoFolder folder) {
return new DirIdCacheKey(folder.getPath());
}
private final String path; private final String path;
private DirIdCacheKey(String path) { private DirIdCacheKey(String path) {
this.path = path; this.path = path;
} }
static DirIdCacheKey toKey(CryptoFolder folder) {
return new DirIdCacheKey(folder.getPath());
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == this) if (obj == this) {
return true; return true;
if (obj == null || getClass() != obj.getClass()) }
if (obj == null || getClass() != obj.getClass()) {
return false; return false;
}
return internalEquals((DirIdCacheKey) obj); return internalEquals((DirIdCacheKey) obj);
} }

View File

@ -39,10 +39,6 @@ class DirIdCacheFormatPre7 implements DirIdCache {
private static class DirIdCacheKey { private static class DirIdCacheKey {
static DirIdCacheKey toKey(CryptoFolder folder) {
return new DirIdCacheKey(folder.getDirFile());
}
private final String path; private final String path;
private final Date modified; private final Date modified;
@ -56,16 +52,22 @@ class DirIdCacheFormatPre7 implements DirIdCache {
this.modified = null; this.modified = null;
} }
static DirIdCacheKey toKey(CryptoFolder folder) {
return new DirIdCacheKey(folder.getDirFile());
}
DirIdCacheKey withoutModified() { DirIdCacheKey withoutModified() {
return new DirIdCacheKey(path); return new DirIdCacheKey(path);
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == this) if (obj == this) {
return true; return true;
if (obj == null || getClass() != obj.getClass()) }
if (obj == null || getClass() != obj.getClass()) {
return false; return false;
}
return internalEquals((DirIdCacheKey) obj); return internalEquals((DirIdCacheKey) obj);
} }

View File

@ -4,10 +4,6 @@ import org.cryptomator.domain.Cloud;
class RootCryptoFolder extends CryptoFolder { class RootCryptoFolder extends CryptoFolder {
public static boolean isRoot(CryptoFolder folder) {
return folder instanceof RootCryptoFolder;
}
private final CryptoCloud cloud; private final CryptoCloud cloud;
public RootCryptoFolder(CryptoCloud cloud) { public RootCryptoFolder(CryptoCloud cloud) {
@ -15,6 +11,10 @@ class RootCryptoFolder extends CryptoFolder {
this.cloud = cloud; this.cloud = cloud;
} }
public static boolean isRoot(CryptoFolder folder) {
return folder instanceof RootCryptoFolder;
}
@Override @Override
public Cloud getCloud() { public Cloud getCloud() {
return cloud; return cloud;

View File

@ -23,6 +23,10 @@ class DropboxClientFactory {
private DbxClientV2 sDbxClient; private DbxClientV2 sDbxClient;
private static Interceptor httpLoggingInterceptor(Context context) {
return new HttpLoggingInterceptor(message -> Timber.tag("OkHttp").d(message), context);
}
public DbxClientV2 getClient(String accessToken, Context context) { public DbxClientV2 getClient(String accessToken, Context context) {
if (sDbxClient == null) { if (sDbxClient == null) {
sDbxClient = createDropboxClient(accessToken, context); sDbxClient = createDropboxClient(accessToken, context);
@ -49,8 +53,4 @@ class DropboxClientFactory {
return new DbxClientV2(requestConfig, accessToken); return new DbxClientV2(requestConfig, accessToken);
} }
private static Interceptor httpLoggingInterceptor(Context context) {
return new HttpLoggingInterceptor(message -> Timber.tag("OkHttp").d(message), context);
}
} }

View File

@ -1,11 +1,11 @@
package org.cryptomator.data.cloud.dropbox; package org.cryptomator.data.cloud.dropbox;
import org.cryptomator.util.Optional;
import com.dropbox.core.v2.files.FileMetadata; import com.dropbox.core.v2.files.FileMetadata;
import com.dropbox.core.v2.files.FolderMetadata; import com.dropbox.core.v2.files.FolderMetadata;
import com.dropbox.core.v2.files.Metadata; import com.dropbox.core.v2.files.Metadata;
import org.cryptomator.util.Optional;
class DropboxCloudNodeFactory { class DropboxCloudNodeFactory {
public static DropboxFile from(DropboxFolder parent, FileMetadata metadata) { public static DropboxFile from(DropboxFolder parent, FileMetadata metadata) {

View File

@ -79,6 +79,14 @@ class DropboxImpl {
sharedPreferencesHandler = new SharedPreferencesHandler(context); sharedPreferencesHandler = new SharedPreferencesHandler(context);
} }
private static void sleepQuietly(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ex) {
throw new FatalBackendException("Error uploading to Dropbox: interrupted during backoff.");
}
}
private DbxClientV2 client() throws AuthenticationException { private DbxClientV2 client() throws AuthenticationException {
return clientFactory.getClient(decrypt(cloud.accessToken()), context); return clientFactory.getClient(decrypt(cloud.accessToken()), context);
} }
@ -186,8 +194,7 @@ class DropboxImpl {
relocationResult.getMetadata()); relocationResult.getMetadata());
} }
public DropboxFile write(DropboxFile file, DataSource data, final ProgressAware<UploadState> progressAware, boolean replace, long size) public DropboxFile write(DropboxFile file, DataSource data, final ProgressAware<UploadState> progressAware, boolean replace, long size) throws AuthenticationException, DbxException, IOException, CloudNodeAlreadyExistsException {
throws AuthenticationException, DbxException, IOException, CloudNodeAlreadyExistsException {
if (exists(file) && !replace) { if (exists(file) && !replace) {
throw new CloudNodeAlreadyExistsException("CloudNode already exists and replace is false"); throw new CloudNodeAlreadyExistsException("CloudNode already exists and replace is false");
} }
@ -237,8 +244,7 @@ class DropboxImpl {
} }
} }
private void chunkedUploadFile(final DropboxFile file, DataSource data, final ProgressAware<UploadState> progressAware, WriteMode writeMode, final long size) private void chunkedUploadFile(final DropboxFile file, DataSource data, final ProgressAware<UploadState> progressAware, WriteMode writeMode, final long size) throws AuthenticationException, DbxException, IOException {
throws AuthenticationException, DbxException, IOException {
// Assert our file is at least the chunk upload size. We make this assumption in the code // Assert our file is at least the chunk upload size. We make this assumption in the code
// below to simplify the logic. // below to simplify the logic.
if (size < CHUNKED_UPLOAD_CHUNK_SIZE) { if (size < CHUNKED_UPLOAD_CHUNK_SIZE) {
@ -457,12 +463,4 @@ class DropboxImpl {
.getCurrentAccount(); .getCurrentAccount();
return currentAccount.getName().getDisplayName(); return currentAccount.getName().getDisplayName();
} }
private static void sleepQuietly(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ex) {
throw new FatalBackendException("Error uploading to Dropbox: interrupted during backoff.");
}
}
} }

View File

@ -3,4 +3,5 @@ package org.cryptomator.data.cloud.dropbox;
import org.cryptomator.domain.CloudNode; import org.cryptomator.domain.CloudNode;
interface DropboxNode extends CloudNode { interface DropboxNode extends CloudNode {
} }

View File

@ -3,4 +3,5 @@ package org.cryptomator.data.cloud.local.file;
import org.cryptomator.domain.CloudNode; import org.cryptomator.domain.CloudNode;
interface LocalNode extends CloudNode { interface LocalNode extends CloudNode {
} }

View File

@ -72,10 +72,12 @@ class LocalStorageAccessFile implements CloudFile, LocalStorageAccessNode {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == this) if (obj == this) {
return true; return true;
if (obj == null || getClass() != obj.getClass()) }
if (obj == null || getClass() != obj.getClass()) {
return false; return false;
}
return internalEquals((LocalStorageAccessFile) obj); return internalEquals((LocalStorageAccessFile) obj);
} }

View File

@ -59,10 +59,12 @@ class LocalStorageAccessFolder implements CloudFolder, LocalStorageAccessNode {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == this) if (obj == this) {
return true; return true;
if (obj == null || getClass() != obj.getClass()) }
if (obj == null || getClass() != obj.getClass()) {
return false; return false;
}
return internalEquals((LocalStorageAccessFolder) obj); return internalEquals((LocalStorageAccessFolder) obj);
} }

View File

@ -3,6 +3,8 @@ package org.cryptomator.data.cloud.local.storageaccessframework;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import androidx.annotation.RequiresApi;
import org.cryptomator.domain.CloudNode; import org.cryptomator.domain.CloudNode;
import org.cryptomator.domain.LocalStorageCloud; import org.cryptomator.domain.LocalStorageCloud;
import org.cryptomator.domain.exception.BackendException; import org.cryptomator.domain.exception.BackendException;
@ -21,8 +23,6 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
import androidx.annotation.RequiresApi;
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public class LocalStorageAccessFrameworkContentRepository implements CloudContentRepository<LocalStorageCloud, LocalStorageAccessNode, LocalStorageAccessFolder, LocalStorageAccessFile> { public class LocalStorageAccessFrameworkContentRepository implements CloudContentRepository<LocalStorageCloud, LocalStorageAccessNode, LocalStorageAccessFolder, LocalStorageAccessFile> {

View File

@ -1,18 +1,16 @@
package org.cryptomator.data.cloud.local.storageaccessframework; package org.cryptomator.data.cloud.local.storageaccessframework;
import static org.cryptomator.data.cloud.local.storageaccessframework.LocalStorageAccessFrameworkNodeFactory.from; import android.content.ContentResolver;
import static org.cryptomator.data.util.CopyStream.closeQuietly; import android.content.Context;
import static org.cryptomator.data.util.CopyStream.copyStreamToStream; import android.content.UriPermission;
import static org.cryptomator.domain.usecases.ProgressAware.NO_OP_PROGRESS_AWARE; import android.database.Cursor;
import static org.cryptomator.domain.usecases.cloud.Progress.progress; import android.net.Uri;
import android.os.Build;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import java.io.FileNotFoundException; import androidx.annotation.RequiresApi;
import java.io.FileOutputStream; import androidx.documentfile.provider.DocumentFile;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.cryptomator.data.util.TransferredBytesAwareInputStream; import org.cryptomator.data.util.TransferredBytesAwareInputStream;
import org.cryptomator.data.util.TransferredBytesAwareOutputStream; import org.cryptomator.data.util.TransferredBytesAwareOutputStream;
@ -35,20 +33,22 @@ import org.cryptomator.util.Supplier;
import org.cryptomator.util.file.MimeType; import org.cryptomator.util.file.MimeType;
import org.cryptomator.util.file.MimeTypes; import org.cryptomator.util.file.MimeTypes;
import android.content.ContentResolver; import java.io.FileNotFoundException;
import android.content.Context; import java.io.FileOutputStream;
import android.content.UriPermission; import java.io.IOException;
import android.database.Cursor; import java.io.InputStream;
import android.net.Uri; import java.io.OutputStream;
import android.os.Build; import java.util.ArrayList;
import android.provider.DocumentsContract; import java.util.List;
import android.provider.DocumentsContract.Document;
import androidx.annotation.RequiresApi;
import androidx.documentfile.provider.DocumentFile;
import timber.log.Timber; import timber.log.Timber;
import static org.cryptomator.data.cloud.local.storageaccessframework.LocalStorageAccessFrameworkNodeFactory.from;
import static org.cryptomator.data.util.CopyStream.closeQuietly;
import static org.cryptomator.data.util.CopyStream.copyStreamToStream;
import static org.cryptomator.domain.usecases.ProgressAware.NO_OP_PROGRESS_AWARE;
import static org.cryptomator.domain.usecases.cloud.Progress.progress;
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class LocalStorageAccessFrameworkImpl { class LocalStorageAccessFrameworkImpl {
@ -178,7 +178,7 @@ class LocalStorageAccessFrameworkImpl {
.query( // .query( //
DocumentsContract.buildChildDocumentsUriUsingTree( // DocumentsContract.buildChildDocumentsUriUsingTree( //
parent.getUri(), // parent.getUri(), //
parent.getDocumentId()), parent.getDocumentId()), //
new String[] {Document.COLUMN_DISPLAY_NAME, // cursor position 0 new String[] {Document.COLUMN_DISPLAY_NAME, // cursor position 0
Document.COLUMN_MIME_TYPE, // cursor position 1 Document.COLUMN_MIME_TYPE, // cursor position 1
Document.COLUMN_SIZE, // cursor position 2 Document.COLUMN_SIZE, // cursor position 2
@ -230,7 +230,7 @@ class LocalStorageAccessFrameworkImpl {
.query( // .query( //
DocumentsContract.buildChildDocumentsUriUsingTree( // DocumentsContract.buildChildDocumentsUriUsingTree( //
folder.getUri(), // folder.getUri(), //
folder.getDocumentId()), folder.getDocumentId()), //
new String[] { // new String[] { //
Document.COLUMN_DISPLAY_NAME, // cursor position 0 Document.COLUMN_DISPLAY_NAME, // cursor position 0
Document.COLUMN_MIME_TYPE, // cursor position 1 Document.COLUMN_MIME_TYPE, // cursor position 1

View File

@ -4,13 +4,13 @@ import android.database.Cursor;
import android.os.Build; import android.os.Build;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
import androidx.annotation.RequiresApi;
import androidx.documentfile.provider.DocumentFile;
import org.cryptomator.util.Optional; import org.cryptomator.util.Optional;
import java.util.Date; import java.util.Date;
import androidx.annotation.RequiresApi;
import androidx.documentfile.provider.DocumentFile;
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class LocalStorageAccessFrameworkNodeFactory { class LocalStorageAccessFrameworkNodeFactory {

View File

@ -3,11 +3,11 @@ package org.cryptomator.data.cloud.local.storageaccessframework;
import android.os.Build; import android.os.Build;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
import androidx.annotation.RequiresApi;
import org.cryptomator.domain.Cloud; import org.cryptomator.domain.Cloud;
import org.cryptomator.domain.LocalStorageCloud; import org.cryptomator.domain.LocalStorageCloud;
import androidx.annotation.RequiresApi;
import static android.net.Uri.parse; import static android.net.Uri.parse;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@ -23,9 +23,7 @@ public class RootLocalStorageAccessFolder extends LocalStorageAccessFolder {
parse(localStorageCloud.rootUri())), // parse(localStorageCloud.rootUri())), //
DocumentsContract.buildChildDocumentsUriUsingTree( // DocumentsContract.buildChildDocumentsUriUsingTree( //
parse(localStorageCloud.rootUri()), // parse(localStorageCloud.rootUri()), //
DocumentsContract.getTreeDocumentId( // DocumentsContract.getTreeDocumentId(parse(localStorageCloud.rootUri()))).toString());
parse(localStorageCloud.rootUri())))
.toString());
this.localStorageCloud = localStorageCloud; this.localStorageCloud = localStorageCloud;
} }

View File

@ -29,18 +29,17 @@ public final class HttpLoggingInterceptor implements Interceptor {
"Cookie", // "Cookie", //
"Set-Cookie" // "Set-Cookie" //
); );
private final Logger logger;
public interface Logger { private final Context context;
void log(String message);
}
public HttpLoggingInterceptor(Logger logger, Context context) { public HttpLoggingInterceptor(Logger logger, Context context) {
this.logger = logger; this.logger = logger;
this.context = context; this.context = context;
} }
private final Logger logger; private static boolean debugModeEnabled(Context context) {
private final Context context; return getDefaultSharedPreferences(context).getBoolean("debugMode", false);
}
@NotNull @NotNull
@Override @Override
@ -137,11 +136,12 @@ public final class HttpLoggingInterceptor implements Interceptor {
} }
} }
private static boolean debugModeEnabled(Context context) {
return getDefaultSharedPreferences(context).getBoolean("debugMode", false);
}
private boolean isExcludedHeader(String name) { private boolean isExcludedHeader(String name) {
return EXCLUDED_HEADERS.contains(name); return EXCLUDED_HEADERS.contains(name);
} }
public interface Logger {
void log(String message);
}
} }

View File

@ -22,11 +22,9 @@ import static org.cryptomator.data.util.NetworkTimeout.WRITE;
public class OnedriveClientFactory { public class OnedriveClientFactory {
private static OnedriveClientFactory instance;
private final AtomicReference<IGraphServiceClient> graphServiceClient = new AtomicReference<>(); private final AtomicReference<IGraphServiceClient> graphServiceClient = new AtomicReference<>();
private final IAuthenticationAdapter authenticationAdapter; private final IAuthenticationAdapter authenticationAdapter;
private static OnedriveClientFactory instance;
private final Context context; private final Context context;
private OnedriveClientFactory(Context context, String refreshToken) { private OnedriveClientFactory(Context context, String refreshToken) {
@ -41,6 +39,10 @@ public class OnedriveClientFactory {
return instance; return instance;
} }
private static Interceptor httpLoggingInterceptor(Context context) {
return new HttpLoggingInterceptor(message -> Timber.tag("OkHttp").d(message), context);
}
public IGraphServiceClient client() { public IGraphServiceClient client() {
if (graphServiceClient.get() == null) { if (graphServiceClient.get() == null) {
@ -67,10 +69,6 @@ public class OnedriveClientFactory {
return graphServiceClient.get(); return graphServiceClient.get();
} }
private static Interceptor httpLoggingInterceptor(Context context) {
return new HttpLoggingInterceptor(message -> Timber.tag("OkHttp").d(message), context);
}
public synchronized IAuthenticationAdapter getAuthenticationAdapter() { public synchronized IAuthenticationAdapter getAuthenticationAdapter() {
return authenticationAdapter; return authenticationAdapter;
} }

View File

@ -21,20 +21,6 @@
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
package org.cryptomator.data.cloud.onedrive; package org.cryptomator.data.cloud.onedrive;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.microsoft.graph.authentication.IAuthenticationProvider; import com.microsoft.graph.authentication.IAuthenticationProvider;
import com.microsoft.graph.concurrency.ICallback; import com.microsoft.graph.concurrency.ICallback;
@ -63,6 +49,20 @@ import com.microsoft.graph.serializer.ISerializer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Protocol; import okhttp3.Protocol;
@ -134,6 +134,43 @@ public class OnedriveHttpProvider implements IHttpProvider {
this.corehttpClient = httpClient; this.corehttpClient = httpClient;
} }
/**
* Reads in a stream and converts it into a string
*
* @param input the response body stream
* @return the string result
*/
public static String streamToString(final InputStream input) {
final String httpStreamEncoding = "UTF-8";
final String endOfFile = "\\A";
final Scanner scanner = new Scanner(input, httpStreamEncoding);
String scannerString = "";
try {
scanner.useDelimiter(endOfFile);
scannerString = scanner.next();
} finally {
scanner.close();
}
return scannerString;
}
/**
* Searches for the given header in a list of HeaderOptions
*
* @param headers the list of headers to search through
* @param header the header name to search for (case insensitive)
* @return true if the header has already been set
*/
@VisibleForTesting
static boolean hasHeader(List<HeaderOption> headers, String header) {
for (HeaderOption option : headers) {
if (option.getName().equalsIgnoreCase(header)) {
return true;
}
}
return false;
}
/** /**
* Gets the serializer for this HTTP provider * Gets the serializer for this HTTP provider
* *
@ -201,8 +238,7 @@ public class OnedriveHttpProvider implements IHttpProvider {
* @return the result from the request * @return the result from the request
* @throws ClientException this exception occurs if the request was unable to complete for any reason * @throws ClientException this exception occurs if the request was unable to complete for any reason
*/ */
public <Result, Body, DeserializeType> Result send(final IHttpRequest request, final Class<Result> resultClass, final Body serializable, final IStatefulResponseHandler<Result, DeserializeType> handler) public <Result, Body, DeserializeType> Result send(final IHttpRequest request, final Class<Result> resultClass, final Body serializable, final IStatefulResponseHandler<Result, DeserializeType> handler) throws ClientException {
throws ClientException {
return sendRequestInternal(request, resultClass, serializable, null, handler); return sendRequestInternal(request, resultClass, serializable, null, handler);
} }
@ -229,10 +265,8 @@ public class OnedriveHttpProvider implements IHttpProvider {
} }
// Request level middleware options // Request level middleware options
RedirectOptions redirectOptions = new RedirectOptions(request.getMaxRedirects() > 0 ? request.getMaxRedirects() : this.connectionConfig.getMaxRedirects(), RedirectOptions redirectOptions = new RedirectOptions(request.getMaxRedirects() > 0 ? request.getMaxRedirects() : this.connectionConfig.getMaxRedirects(), request.getShouldRedirect() != null ? request.getShouldRedirect() : this.connectionConfig.getShouldRedirect());
request.getShouldRedirect() != null ? request.getShouldRedirect() : this.connectionConfig.getShouldRedirect()); RetryOptions retryOptions = new RetryOptions(request.getShouldRetry() != null ? request.getShouldRetry() : this.connectionConfig.getShouldRetry(), request.getMaxRetries() > 0 ? request.getMaxRetries() : this.connectionConfig.getMaxRetries(), request.getDelay() > 0 ? request.getDelay() : this.connectionConfig.getDelay());
RetryOptions retryOptions = new RetryOptions(request.getShouldRetry() != null ? request.getShouldRetry() : this.connectionConfig.getShouldRetry(),
request.getMaxRetries() > 0 ? request.getMaxRetries() : this.connectionConfig.getMaxRetries(), request.getDelay() > 0 ? request.getDelay() : this.connectionConfig.getDelay());
Request coreHttpRequest = convertIHttpRequestToOkHttpRequest(request); Request coreHttpRequest = convertIHttpRequestToOkHttpRequest(request);
Request.Builder corehttpRequestBuilder = coreHttpRequest.newBuilder().tag(RedirectOptions.class, redirectOptions).tag(RetryOptions.class, retryOptions); Request.Builder corehttpRequestBuilder = coreHttpRequest.newBuilder().tag(RedirectOptions.class, redirectOptions).tag(RetryOptions.class, retryOptions);
@ -343,8 +377,7 @@ public class OnedriveHttpProvider implements IHttpProvider {
* @throws ClientException an exception occurs if the request was unable to complete for any reason * @throws ClientException an exception occurs if the request was unable to complete for any reason
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <Result, Body, DeserializeType> Result sendRequestInternal(final IHttpRequest request, final Class<Result> resultClass, final Body serializable, final IProgressCallback<? super Result> progress, private <Result, Body, DeserializeType> Result sendRequestInternal(final IHttpRequest request, final Class<Result> resultClass, final Body serializable, final IProgressCallback<? super Result> progress, final IStatefulResponseHandler<Result, DeserializeType> handler) throws ClientException {
final IStatefulResponseHandler<Result, DeserializeType> handler) throws ClientException {
try { try {
if (this.connectionConfig == null) { if (this.connectionConfig == null) {
@ -352,8 +385,7 @@ public class OnedriveHttpProvider implements IHttpProvider {
} }
if (this.corehttpClient == null) { if (this.corehttpClient == null) {
final ICoreAuthenticationProvider authProvider = request1 -> request1; final ICoreAuthenticationProvider authProvider = request1 -> request1;
this.corehttpClient = HttpClients.createDefault(authProvider).newBuilder().connectTimeout(connectionConfig.getConnectTimeout(), TimeUnit.MILLISECONDS) this.corehttpClient = HttpClients.createDefault(authProvider).newBuilder().connectTimeout(connectionConfig.getConnectTimeout(), TimeUnit.MILLISECONDS).readTimeout(connectionConfig.getReadTimeout(), TimeUnit.MILLISECONDS).followRedirects(false) // TODO https://github.com/microsoftgraph/msgraph-sdk-java/issues/516
.readTimeout(connectionConfig.getReadTimeout(), TimeUnit.MILLISECONDS).followRedirects(false) // TODO https://github.com/microsoftgraph/msgraph-sdk-java/issues/516
.protocols(Collections.singletonList(Protocol.HTTP_1_1)) // https://stackoverflow.com/questions/62031298/sockettimeout-on-java-11-but-not-on-java-8 .protocols(Collections.singletonList(Protocol.HTTP_1_1)) // https://stackoverflow.com/questions/62031298/sockettimeout-on-java-11-but-not-on-java-8
.build(); .build();
} }
@ -399,8 +431,9 @@ public class OnedriveHttpProvider implements IHttpProvider {
final Map<String, String> headers = responseHeadersHelper.getResponseHeadersAsMapStringString(response); final Map<String, String> headers = responseHeadersHelper.getResponseHeadersAsMapStringString(response);
if (response.body() == null || response.body().contentLength() == 0) if (response.body() == null || response.body().contentLength() == 0) {
return (Result) null; return (Result) null;
}
final String contentType = headers.get(Constants.CONTENT_TYPE_HEADER_NAME); final String contentType = headers.get(Constants.CONTENT_TYPE_HEADER_NAME);
if (contentType != null && resultClass != InputStream.class && contentType.contains(Constants.JSON_CONTENT_TYPE)) { if (contentType != null && resultClass != InputStream.class && contentType.contains(Constants.JSON_CONTENT_TYPE)) {
@ -416,15 +449,17 @@ public class OnedriveHttpProvider implements IHttpProvider {
} finally { } finally {
if (!isBinaryStreamInput) { if (!isBinaryStreamInput) {
try { try {
if (in != null) if (in != null) {
in.close(); in.close();
}
} catch (IOException e) { } catch (IOException e) {
logger.logError(e.getMessage(), e); logger.logError(e.getMessage(), e);
} }
if (response != null) if (response != null) {
response.close(); response.close();
} }
} }
}
} catch (final GraphServiceException ex) { } catch (final GraphServiceException ex) {
final boolean shouldLogVerbosely = logger.getLoggingLevel() == LoggerLevel.DEBUG; final boolean shouldLogVerbosely = logger.getLoggingLevel() == LoggerLevel.DEBUG;
logger.logError("Graph service exception " + ex.getMessage(shouldLogVerbosely), ex); logger.logError("Graph service exception " + ex.getMessage(shouldLogVerbosely), ex);
@ -502,43 +537,6 @@ public class OnedriveHttpProvider implements IHttpProvider {
return handleJsonResponse(in, responseHeaders, clazz); return handleJsonResponse(in, responseHeaders, clazz);
} }
/**
* Reads in a stream and converts it into a string
*
* @param input the response body stream
* @return the string result
*/
public static String streamToString(final InputStream input) {
final String httpStreamEncoding = "UTF-8";
final String endOfFile = "\\A";
final Scanner scanner = new Scanner(input, httpStreamEncoding);
String scannerString = "";
try {
scanner.useDelimiter(endOfFile);
scannerString = scanner.next();
} finally {
scanner.close();
}
return scannerString;
}
/**
* Searches for the given header in a list of HeaderOptions
*
* @param headers the list of headers to search through
* @param header the header name to search for (case insensitive)
* @return true if the header has already been set
*/
@VisibleForTesting
static boolean hasHeader(List<HeaderOption> headers, String header) {
for (HeaderOption option : headers) {
if (option.getName().equalsIgnoreCase(header)) {
return true;
}
}
return false;
}
@VisibleForTesting @VisibleForTesting
public ILogger getLogger() { public ILogger getLogger() {
return logger; return logger;

View File

@ -68,11 +68,10 @@ class OnedriveImpl {
private static final long CHUNKED_UPLOAD_MAX_SIZE = 4L << 20; private static final long CHUNKED_UPLOAD_MAX_SIZE = 4L << 20;
private static final int CHUNKED_UPLOAD_CHUNK_SIZE = 327680 * 32; private static final int CHUNKED_UPLOAD_CHUNK_SIZE = 327680 * 32;
private static final int CHUNKED_UPLOAD_MAX_ATTEMPTS = 5; private static final int CHUNKED_UPLOAD_MAX_ATTEMPTS = 5;
private final OnedriveCloud cloud;
private final Context context;
private static final String REPLACE_MODE = "replace"; private static final String REPLACE_MODE = "replace";
private static final String NON_REPLACING_MODE = "rename"; private static final String NON_REPLACING_MODE = "rename";
private final OnedriveCloud cloud;
private final Context context;
private final OnedriveIdCache nodeInfoCache; private final OnedriveIdCache nodeInfoCache;
private final OnedriveClientFactory clientFactory; private final OnedriveClientFactory clientFactory;
private final SharedPreferencesHandler sharedPreferencesHandler; private final SharedPreferencesHandler sharedPreferencesHandler;

View File

@ -28,6 +28,7 @@ package org.cryptomator.data.cloud.onedrive.graph;
* @param <Result> the result type of the successful action * @param <Result> the result type of the successful action
*/ */
public interface ICallback<Result> { public interface ICallback<Result> {
/** /**
* How successful results are handled * How successful results are handled
* *

View File

@ -39,6 +39,18 @@ public abstract class MSAAuthAndroidAdapter implements IAuthenticationAdapter {
* The live auth client. * The live auth client.
*/ */
private final LiveAuthClient mLiveAuthClient; private final LiveAuthClient mLiveAuthClient;
private Context context;
/**
* Create a new instance of the provider
*
* @param context the application context instance
* @param refreshToken
*/
protected MSAAuthAndroidAdapter(final Context context, String refreshToken) {
this.context = context;
mLiveAuthClient = new LiveAuthClient(context, getClientId(), Arrays.asList(getScopes()), MicrosoftOAuth2Endpoint.getInstance(), refreshToken);
}
/** /**
* The client id for this authenticator. * The client id for this authenticator.
@ -56,19 +68,6 @@ public abstract class MSAAuthAndroidAdapter implements IAuthenticationAdapter {
*/ */
protected abstract String[] getScopes(); protected abstract String[] getScopes();
private Context context;
/**
* Create a new instance of the provider
*
* @param context the application context instance
* @param refreshToken
*/
protected MSAAuthAndroidAdapter(final Context context, String refreshToken) {
this.context = context;
mLiveAuthClient = new LiveAuthClient(context, getClientId(), Arrays.asList(getScopes()), MicrosoftOAuth2Endpoint.getInstance(), refreshToken);
}
@Override @Override
public void authenticateRequest(final IHttpRequest request) { public void authenticateRequest(final IHttpRequest request) {
Timber.tag("MSAAuthAndroidAdapter").d("Authenticating request, %s", request.getRequestUrl()); Timber.tag("MSAAuthAndroidAdapter").d("Authenticating request, %s", request.getRequestUrl());
@ -181,8 +180,9 @@ public abstract class MSAAuthAndroidAdapter implements IAuthenticationAdapter {
} }
private String encrypt(String refreshToken) { private String encrypt(String refreshToken) {
if (refreshToken == null) if (refreshToken == null) {
return null; return null;
}
return CredentialCryptor // return CredentialCryptor //
.getInstance(context) // .getInstance(context) //
.encrypt(refreshToken); .encrypt(refreshToken);

View File

@ -7,6 +7,7 @@ import com.microsoft.services.msa.OAuthConfig;
import org.cryptomator.data.BuildConfig; import org.cryptomator.data.BuildConfig;
class MicrosoftOAuth2Endpoint implements OAuthConfig { class MicrosoftOAuth2Endpoint implements OAuthConfig {
/** /**
* The current instance of this class * The current instance of this class
*/ */

View File

@ -41,9 +41,8 @@ import static org.cryptomator.util.ExceptionUtil.extract;
@Singleton @Singleton
class WebDavCloudContentRepository extends InterceptingCloudContentRepository<WebDavCloud, WebDavNode, WebDavFolder, WebDavFile> { class WebDavCloudContentRepository extends InterceptingCloudContentRepository<WebDavCloud, WebDavNode, WebDavFolder, WebDavFile> {
private final WebDavCloud cloud;
private static final CharSequence START_OF_CERTIFICATE = "-----BEGIN CERTIFICATE-----"; private static final CharSequence START_OF_CERTIFICATE = "-----BEGIN CERTIFICATE-----";
private final WebDavCloud cloud;
WebDavCloudContentRepository(WebDavCloud cloud, ConnectionHandlerHandlerImpl connectionHandlerHandler) { WebDavCloudContentRepository(WebDavCloud cloud, ConnectionHandlerHandlerImpl connectionHandlerHandler) {
super(new Intercepted(cloud, connectionHandlerHandler)); super(new Intercepted(cloud, connectionHandlerHandler));

View File

@ -174,42 +174,6 @@ class WebDavImpl {
connectionHandler.checkAuthenticationAndServerCompatibility(url); connectionHandler.checkAuthenticationAndServerCompatibility(url);
} }
private static abstract class TransferredBytesAwareDataSource implements DataSource {
private final DataSource data;
TransferredBytesAwareDataSource(DataSource data) {
this.data = data;
}
@Override
public Optional<Long> size(Context context) {
return data.size(context);
}
@Override
public InputStream open(Context context) throws IOException {
return new TransferredBytesAwareInputStream(data.open(context)) {
@Override
public void bytesTransferred(long transferred) {
TransferredBytesAwareDataSource.this.bytesTrasferred(transferred);
}
};
}
@Override
public void close() throws IOException {
data.close();
}
public abstract void bytesTrasferred(long transferred);
@Override
public DataSource decorate(DataSource delegate) {
return delegate;
}
}
public void read(final CloudFile file, OutputStream data, final ProgressAware<DownloadState> progressAware) throws BackendException, IOException { public void read(final CloudFile file, OutputStream data, final ProgressAware<DownloadState> progressAware) throws BackendException, IOException {
progressAware.onProgress(Progress.started(DownloadState.download(file))); progressAware.onProgress(Progress.started(DownloadState.download(file)));
@ -251,4 +215,40 @@ class WebDavImpl {
checkAuthenticationAndServerCompatibility(cloud.url()); checkAuthenticationAndServerCompatibility(cloud.url());
return cloud.url(); return cloud.url();
} }
private static abstract class TransferredBytesAwareDataSource implements DataSource {
private final DataSource data;
TransferredBytesAwareDataSource(DataSource data) {
this.data = data;
}
@Override
public Optional<Long> size(Context context) {
return data.size(context);
}
@Override
public InputStream open(Context context) throws IOException {
return new TransferredBytesAwareInputStream(data.open(context)) {
@Override
public void bytesTransferred(long transferred) {
TransferredBytesAwareDataSource.this.bytesTrasferred(transferred);
}
};
}
@Override
public void close() throws IOException {
data.close();
}
public abstract void bytesTrasferred(long transferred);
@Override
public DataSource decorate(DataSource delegate) {
return delegate;
}
}
} }

View File

@ -9,6 +9,7 @@ import javax.inject.Singleton;
@Singleton @Singleton
public class ConnectionHandlerFactory { public class ConnectionHandlerFactory {
private final Context context; private final Context context;
@Inject @Inject

View File

@ -2,10 +2,10 @@ package org.cryptomator.data.cloud.webdav.network;
import android.content.Context; import android.content.Context;
import java.io.IOException;
import org.cryptomator.domain.usecases.cloud.DataSource; import org.cryptomator.domain.usecases.cloud.DataSource;
import java.io.IOException;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okio.BufferedSink; import okio.BufferedSink;
@ -13,10 +13,6 @@ import okio.Okio;
class DataSourceBasedRequestBody extends RequestBody { class DataSourceBasedRequestBody extends RequestBody {
public static RequestBody from(Context context, DataSource data) {
return new DataSourceBasedRequestBody(context, data);
}
private final Context context; private final Context context;
private final DataSource data; private final DataSource data;
@ -25,6 +21,10 @@ class DataSourceBasedRequestBody extends RequestBody {
this.data = data; this.data = data;
} }
public static RequestBody from(Context context, DataSource data) {
return new DataSourceBasedRequestBody(context, data);
}
@Override @Override
public long contentLength() { public long contentLength() {
return data.size(context).get(); return data.size(context).get();

View File

@ -12,6 +12,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
class PropfindEntryData { class PropfindEntryData {
private static final Pattern URI_PATTERN = Pattern.compile("^[a-z]+://[^/]+/(.*)$"); private static final Pattern URI_PATTERN = Pattern.compile("^[a-z]+://[^/]+/(.*)$");
private String path; private String path;
@ -21,11 +22,6 @@ class PropfindEntryData {
private Optional<Date> lastModified = Optional.empty(); private Optional<Date> lastModified = Optional.empty();
private Optional<Long> size = Optional.empty(); private Optional<Long> size = Optional.empty();
public void setPath(String pathOrUri) {
this.path = extractPath(pathOrUri);
this.pathSegments = path.split("/");
}
private String extractPath(String pathOrUri) { private String extractPath(String pathOrUri) {
Matcher matcher = URI_PATTERN.matcher(pathOrUri); Matcher matcher = URI_PATTERN.matcher(pathOrUri);
if (matcher.matches()) { if (matcher.matches()) {
@ -41,26 +37,31 @@ class PropfindEntryData {
this.lastModified = lastModified; this.lastModified = lastModified;
} }
public void setSize(Optional<Long> size) {
this.size = size;
}
public void setFile(boolean file) {
this.file = file;
}
public String getPath() { public String getPath() {
return path; return path;
} }
public void setPath(String pathOrUri) {
this.path = extractPath(pathOrUri);
this.pathSegments = path.split("/");
}
public Optional<Long> getSize() { public Optional<Long> getSize() {
return size; return size;
} }
public void setSize(Optional<Long> size) {
this.size = size;
}
private boolean isFile() { private boolean isFile() {
return file; return file;
} }
public void setFile(boolean file) {
this.file = file;
}
public WebDavNode toCloudNode(WebDavFolder parent) { public WebDavNode toCloudNode(WebDavFolder parent) {
if (isFile()) { if (isFile()) {
return new WebDavFile(parent, getName(), size, lastModified); return new WebDavFile(parent, getName(), size, lastModified);

View File

@ -1,14 +1,6 @@
package org.cryptomator.data.cloud.webdav.network; package org.cryptomator.data.cloud.webdav.network;
import static java.util.Collections.sort; import android.content.Context;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.cryptomator.data.cloud.webdav.WebDavFolder; import org.cryptomator.data.cloud.webdav.WebDavFolder;
import org.cryptomator.data.cloud.webdav.WebDavNode; import org.cryptomator.data.cloud.webdav.WebDavNode;
@ -27,7 +19,13 @@ import org.cryptomator.domain.exception.UnauthorizedException;
import org.cryptomator.domain.usecases.cloud.DataSource; import org.cryptomator.domain.usecases.cloud.DataSource;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import android.content.Context; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.Request; import okhttp3.Request;
@ -35,10 +33,13 @@ import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
import okhttp3.ResponseBody; import okhttp3.ResponseBody;
import static java.util.Collections.sort;
class WebDavClient { class WebDavClient {
private final Context context; private final Context context;
private final WebDavCompatibleHttpClient httpClient; private final WebDavCompatibleHttpClient httpClient;
private final Comparator<PropfindEntryData> ASCENDING_BY_DEPTH = (o1, o2) -> o1.getDepth() - o2.getDepth();
WebDavClient(Context context, WebDavCompatibleHttpClient httpClient) { WebDavClient(Context context, WebDavCompatibleHttpClient httpClient) {
this.context = context; this.context = context;
@ -302,8 +303,6 @@ class WebDavClient {
return entryData.size() >= 1 ? entryData.get(0).toCloudNode(requestedFolder) : null; return entryData.size() >= 1 ? entryData.get(0).toCloudNode(requestedFolder) : null;
} }
private final Comparator<PropfindEntryData> ASCENDING_BY_DEPTH = (o1, o2) -> o1.getDepth() - o2.getDepth();
private enum PropfindDepth { private enum PropfindDepth {
ZERO("0"), // ZERO("0"), //
ONE("1"), // ONE("1"), //

View File

@ -1,24 +1,8 @@
package org.cryptomator.data.cloud.webdav.network; package org.cryptomator.data.cloud.webdav.network;
import static com.google.common.net.HttpHeaders.CACHE_CONTROL; import android.content.Context;
import static org.cryptomator.data.util.NetworkTimeout.CONNECTION; import android.net.ConnectivityManager;
import static org.cryptomator.data.util.NetworkTimeout.READ; import android.net.NetworkInfo;
import static org.cryptomator.data.util.NetworkTimeout.WRITE;
import static org.cryptomator.util.file.LruFileCacheUtil.Cache.WEBDAV;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.X509TrustManager;
import org.cryptomator.data.cloud.okhttplogging.HttpLoggingInterceptor;
import org.cryptomator.domain.WebDavCloud;
import org.cryptomator.domain.exception.UnableToDecryptWebdavPasswordException;
import org.cryptomator.util.SharedPreferencesHandler;
import org.cryptomator.util.crypto.CredentialCryptor;
import org.cryptomator.util.file.LruFileCacheUtil;
import com.burgstaller.okhttp.AuthenticationCacheInterceptor; import com.burgstaller.okhttp.AuthenticationCacheInterceptor;
import com.burgstaller.okhttp.CachingAuthenticatorDecorator; import com.burgstaller.okhttp.CachingAuthenticatorDecorator;
@ -28,9 +12,19 @@ import com.burgstaller.okhttp.digest.CachingAuthenticator;
import com.burgstaller.okhttp.digest.Credentials; import com.burgstaller.okhttp.digest.Credentials;
import com.burgstaller.okhttp.digest.DigestAuthenticator; import com.burgstaller.okhttp.digest.DigestAuthenticator;
import android.content.Context; import org.cryptomator.data.cloud.okhttplogging.HttpLoggingInterceptor;
import android.net.ConnectivityManager; import org.cryptomator.domain.WebDavCloud;
import android.net.NetworkInfo; import org.cryptomator.domain.exception.UnableToDecryptWebdavPasswordException;
import org.cryptomator.util.SharedPreferencesHandler;
import org.cryptomator.util.crypto.CredentialCryptor;
import org.cryptomator.util.file.LruFileCacheUtil;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.X509TrustManager;
import okhttp3.Authenticator; import okhttp3.Authenticator;
import okhttp3.Cache; import okhttp3.Cache;
@ -41,6 +35,12 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
import static com.google.common.net.HttpHeaders.CACHE_CONTROL;
import static org.cryptomator.data.util.NetworkTimeout.CONNECTION;
import static org.cryptomator.data.util.NetworkTimeout.READ;
import static org.cryptomator.data.util.NetworkTimeout.WRITE;
import static org.cryptomator.util.file.LruFileCacheUtil.Cache.WEBDAV;
class WebDavCompatibleHttpClient { class WebDavCompatibleHttpClient {
private final WebDavRedirectHandler webDavRedirectHandler; private final WebDavRedirectHandler webDavRedirectHandler;
@ -50,14 +50,6 @@ class WebDavCompatibleHttpClient {
this.webDavRedirectHandler = new WebDavRedirectHandler(httpClientFor(cloud, context, sharedPreferencesHandler.useLruCache(), sharedPreferencesHandler.lruCacheSize())); this.webDavRedirectHandler = new WebDavRedirectHandler(httpClientFor(cloud, context, sharedPreferencesHandler.useLruCache(), sharedPreferencesHandler.lruCacheSize()));
} }
Response execute(Request.Builder requestBuilder) throws IOException {
return execute(requestBuilder.build());
}
private Response execute(Request request) throws IOException {
return webDavRedirectHandler.executeFollowingRedirects(request);
}
private static OkHttpClient httpClientFor(WebDavCloud webDavCloud, Context context, boolean useLruCache, int lruCacheSize) { private static OkHttpClient httpClientFor(WebDavCloud webDavCloud, Context context, boolean useLruCache, int lruCacheSize) {
final Map<String, CachingAuthenticator> authCache = new ConcurrentHashMap<>(); final Map<String, CachingAuthenticator> authCache = new ConcurrentHashMap<>();
@ -162,4 +154,12 @@ class WebDavCompatibleHttpClient {
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected(); return activeNetworkInfo != null && activeNetworkInfo.isConnected();
} }
Response execute(Request.Builder requestBuilder) throws IOException {
return execute(requestBuilder.build());
}
private Response execute(Request request) throws IOException {
return webDavRedirectHandler.executeFollowingRedirects(request);
}
} }

View File

@ -31,6 +31,10 @@ class DatabaseUpgrades {
upgrade3To4); upgrade3To4);
} }
private static Comparator<DatabaseUpgrade> reverseOrder() {
return (a, b) -> b.compareTo(a);
}
private Map<Integer, List<DatabaseUpgrade>> defineUpgrades(DatabaseUpgrade... upgrades) { private Map<Integer, List<DatabaseUpgrade>> defineUpgrades(DatabaseUpgrade... upgrades) {
Map<Integer, List<DatabaseUpgrade>> result = new HashMap<>(); Map<Integer, List<DatabaseUpgrade>> result = new HashMap<>();
for (DatabaseUpgrade upgrade : upgrades) { for (DatabaseUpgrade upgrade : upgrades) {
@ -74,8 +78,4 @@ class DatabaseUpgrades {
} }
return false; return false;
} }
private static Comparator<DatabaseUpgrade> reverseOrder() {
return (a, b) -> b.compareTo(a);
}
} }

View File

@ -76,6 +76,21 @@ class Sql {
return (column, contentValues) -> contentValues.putNull(column); return (column, contentValues) -> contentValues.putNull(column);
} }
private static SQLiteDatabase unwrap(Database wrapped) {
return (SQLiteDatabase) wrapped.getRawDatabase();
}
public interface ValueHolder {
void put(String column, ContentValues contentValues);
}
public interface Criterion {
void appendTo(String column, StringBuilder whereClause, List<String> whereArgs);
}
public static class SqlUpdateBuilder { public static class SqlUpdateBuilder {
private final String tableName; private final String tableName;
@ -129,8 +144,8 @@ class Sql {
public static class SqlUniqueIndexBuilder { public static class SqlUniqueIndexBuilder {
private final String indexName; private final String indexName;
private String table;
private final StringBuilder columns = new StringBuilder(); private final StringBuilder columns = new StringBuilder();
private String table;
private SqlUniqueIndexBuilder(String indexName) { private SqlUniqueIndexBuilder(String indexName) {
this.indexName = indexName; this.indexName = indexName;
@ -173,8 +188,8 @@ class Sql {
public static class SqlAlterTableBuilder { public static class SqlAlterTableBuilder {
private final String table; private final String table;
private String newName;
private final StringBuilder columns = new StringBuilder(); private final StringBuilder columns = new StringBuilder();
private String newName;
private SqlAlterTableBuilder(String table) { private SqlAlterTableBuilder(String table) {
this.table = table; this.table = table;
@ -195,12 +210,10 @@ class Sql {
private static final int NOT_FOUND = -1; private static final int NOT_FOUND = -1;
private final String table; private final String table;
private String[] columns;
private final String[] selectedColumns; private final String[] selectedColumns;
private String sourceTableName;
private final StringBuilder joinClauses = new StringBuilder(); private final StringBuilder joinClauses = new StringBuilder();
private String[] columns;
private String sourceTableName;
private SqlInsertSelectBuilder(String table, String[] columns) { private SqlInsertSelectBuilder(String table, String[] columns) {
this.table = table; this.table = table;
@ -454,19 +467,4 @@ class Sql {
} }
} }
private static SQLiteDatabase unwrap(Database wrapped) {
return (SQLiteDatabase) wrapped.getRawDatabase();
}
public interface ValueHolder {
void put(String column, ContentValues contentValues);
}
public interface Criterion {
void appendTo(String column, StringBuilder whereClause, List<String> whereArgs);
}
} }

View File

@ -1,12 +1,12 @@
package org.cryptomator.data.db; package org.cryptomator.data.db;
import static org.cryptomator.data.db.Sql.createTable; import org.greenrobot.greendao.database.Database;
import static org.cryptomator.data.db.Sql.insertInto;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.greenrobot.greendao.database.Database; import static org.cryptomator.data.db.Sql.createTable;
import static org.cryptomator.data.db.Sql.insertInto;
@Singleton @Singleton
class Upgrade1To2 extends DatabaseUpgrade { class Upgrade1To2 extends DatabaseUpgrade {

View File

@ -22,10 +22,28 @@ public class CloudEntity extends DatabaseEntity {
private String webdavCertificate; private String webdavCertificate;
@Generated(hash = 2078985174)
public CloudEntity(Long id, @NotNull String type, String accessToken, String webdavUrl, String username, String webdavCertificate) {
this.id = id;
this.type = type;
this.accessToken = accessToken;
this.webdavUrl = webdavUrl;
this.username = username;
this.webdavCertificate = webdavCertificate;
}
@Generated(hash = 1354152224)
public CloudEntity() {
}
public String getAccessToken() { public String getAccessToken() {
return this.accessToken; return this.accessToken;
} }
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getType() { public String getType() {
return this.type; return this.type;
} }
@ -42,10 +60,6 @@ public class CloudEntity extends DatabaseEntity {
this.id = id; this.id = id;
} }
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getWebdavUrl() { public String getWebdavUrl() {
return webdavUrl; return webdavUrl;
} }
@ -69,18 +83,4 @@ public class CloudEntity extends DatabaseEntity {
public void setWebdavCertificate(String webdavCertificate) { public void setWebdavCertificate(String webdavCertificate) {
this.webdavCertificate = webdavCertificate; this.webdavCertificate = webdavCertificate;
} }
@Generated(hash = 2078985174)
public CloudEntity(Long id, @NotNull String type, String accessToken, String webdavUrl, String username, String webdavCertificate) {
this.id = id;
this.type = type;
this.accessToken = accessToken;
this.webdavUrl = webdavUrl;
this.username = username;
this.webdavCertificate = webdavCertificate;
}
@Generated(hash = 1354152224)
public CloudEntity() {
}
} }

View File

@ -38,6 +38,10 @@ public class UpdateCheckEntity extends DatabaseEntity {
return id; return id;
} }
public void setId(Long id) {
this.id = id;
}
public String getLicenseToken() { public String getLicenseToken() {
return this.licenseToken; return this.licenseToken;
} }
@ -46,10 +50,6 @@ public class UpdateCheckEntity extends DatabaseEntity {
this.licenseToken = licenseToken; this.licenseToken = licenseToken;
} }
public void setId(Long id) {
this.id = id;
}
public String getVersion() { public String getVersion() {
return this.version; return this.version;
} }

View File

@ -29,6 +29,33 @@ public class VaultEntity extends DatabaseEntity {
private String password; private String password;
private Integer position; private Integer position;
/**
* Used for active entity operations.
*/
@Generated(hash = 941685503)
private transient VaultEntityDao myDao;
/**
* Used to resolve relations
*/
@Generated(hash = 2040040024)
private transient DaoSession daoSession;
@Generated(hash = 229273163)
private transient Long folderCloud__resolvedKey;
@Generated(hash = 825602374)
public VaultEntity(Long id, Long folderCloudId, String folderPath, String folderName, @NotNull String cloudType, String password, Integer position) {
this.id = id;
this.folderCloudId = folderCloudId;
this.folderPath = folderPath;
this.folderName = folderName;
this.cloudType = cloudType;
this.password = password;
this.position = position;
}
@Generated(hash = 691253864)
public VaultEntity() {
}
/** /**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
@ -66,17 +93,9 @@ public class VaultEntity extends DatabaseEntity {
myDao.delete(this); myDao.delete(this);
} }
/** called by internal mechanisms, do not call yourself. */ /**
@Generated(hash = 1482096330) * To-one relationship, resolved on first access.
public void setFolderCloud(CloudEntity folderCloud) { */
synchronized (this) {
this.folderCloud = folderCloud;
folderCloudId = folderCloud == null ? null : folderCloud.getId();
folderCloud__resolvedKey = folderCloudId;
}
}
/** To-one relationship, resolved on first access. */
@Generated(hash = 1508817413) @Generated(hash = 1508817413)
public CloudEntity getFolderCloud() { public CloudEntity getFolderCloud() {
Long __key = this.folderCloudId; Long __key = this.folderCloudId;
@ -95,16 +114,17 @@ public class VaultEntity extends DatabaseEntity {
return folderCloud; return folderCloud;
} }
/** Used for active entity operations. */ /**
@Generated(hash = 941685503) * called by internal mechanisms, do not call yourself.
private transient VaultEntityDao myDao; */
@Generated(hash = 1482096330)
/** Used to resolve relations */ public void setFolderCloud(CloudEntity folderCloud) {
@Generated(hash = 2040040024) synchronized (this) {
private transient DaoSession daoSession; this.folderCloud = folderCloud;
folderCloudId = folderCloud == null ? null : folderCloud.getId();
@Generated(hash = 229273163) folderCloud__resolvedKey = folderCloudId;
private transient Long folderCloud__resolvedKey; }
}
public String getFolderPath() { public String getFolderPath() {
return this.folderPath; return this.folderPath;
@ -169,20 +189,4 @@ public class VaultEntity extends DatabaseEntity {
myDao = daoSession != null ? daoSession.getVaultEntityDao() : null; myDao = daoSession != null ? daoSession.getVaultEntityDao() : null;
} }
@Generated(hash = 825602374)
public VaultEntity(Long id, Long folderCloudId, String folderPath, String folderName, @NotNull String cloudType, String password,
Integer position) {
this.id = id;
this.folderCloudId = folderCloudId;
this.folderPath = folderPath;
this.folderName = folderName;
this.cloudType = cloudType;
this.password = password;
this.position = position;
}
@Generated(hash = 691253864)
public VaultEntity() {
}
} }

View File

@ -17,6 +17,7 @@ import javax.inject.Singleton;
*/ */
@Singleton @Singleton
public class JobExecutor implements ThreadExecutor { public class JobExecutor implements ThreadExecutor {
private static final int INITIAL_POOL_SIZE = 3; private static final int INITIAL_POOL_SIZE = 3;
private static final int MAX_POOL_SIZE = 5; private static final int MAX_POOL_SIZE = 5;
@ -44,6 +45,7 @@ public class JobExecutor implements ThreadExecutor {
} }
private static class JobThreadFactory implements ThreadFactory { private static class JobThreadFactory implements ThreadFactory {
private static final String THREAD_NAME = "android_"; private static final String THREAD_NAME = "android_";
private int counter = 0; private int counter = 0;

View File

@ -14,7 +14,6 @@ public interface CloudContentRepositoryFactory {
* *
* @param cloud the {@link Cloud} to access through the {@code CloudContentRepository} * @param cloud the {@link Cloud} to access through the {@code CloudContentRepository}
* @return the created {@code CloudContentRepository} * @return the created {@code CloudContentRepository}
*
* @throws NoAuthenticationProvidedException if the cloud has not been authenticated * @throws NoAuthenticationProvidedException if the cloud has not been authenticated
* @throws AuthenticationException if an authentication error occurs while accessing the cloud * @throws AuthenticationException if an authentication error occurs while accessing the cloud
*/ */

View File

@ -36,8 +36,7 @@ public class DispatchingCloudContentRepository implements CloudContentRepository
private final CryptoCloudContentRepositoryFactory cryptoCloudContentRepositoryFactory; private final CryptoCloudContentRepositoryFactory cryptoCloudContentRepositoryFactory;
@Inject @Inject
public DispatchingCloudContentRepository(CloudContentRepositoryFactories cloudContentRepositoryFactories, NetworkConnectionCheck networkConnectionCheck, public DispatchingCloudContentRepository(CloudContentRepositoryFactories cloudContentRepositoryFactories, NetworkConnectionCheck networkConnectionCheck, CryptoCloudContentRepositoryFactory cryptoCloudContentRepositoryFactory) {
CryptoCloudContentRepositoryFactory cryptoCloudContentRepositoryFactory) {
this.cloudContentRepositoryFactories = cloudContentRepositoryFactories; this.cloudContentRepositoryFactories = cloudContentRepositoryFactories;
this.networkConnectionCheck = networkConnectionCheck; this.networkConnectionCheck = networkConnectionCheck;
this.cryptoCloudContentRepositoryFactory = cryptoCloudContentRepositoryFactory; this.cryptoCloudContentRepositoryFactory = cryptoCloudContentRepositoryFactory;

View File

@ -2,6 +2,17 @@ package org.cryptomator.data.repository;
import com.google.common.io.BaseEncoding; import com.google.common.io.BaseEncoding;
import org.cryptomator.data.db.Database;
import org.cryptomator.data.db.entities.UpdateCheckEntity;
import org.cryptomator.data.util.UserAgentInterceptor;
import org.cryptomator.domain.exception.BackendException;
import org.cryptomator.domain.exception.FatalBackendException;
import org.cryptomator.domain.exception.update.GeneralUpdateErrorException;
import org.cryptomator.domain.exception.update.SSLHandshakePreAndroid5UpdateCheckException;
import org.cryptomator.domain.repository.UpdateCheckRepository;
import org.cryptomator.domain.usecases.UpdateCheck;
import org.cryptomator.util.Optional;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.security.Key; import java.security.Key;
@ -16,17 +27,6 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import org.cryptomator.data.db.Database;
import org.cryptomator.data.db.entities.UpdateCheckEntity;
import org.cryptomator.data.util.UserAgentInterceptor;
import org.cryptomator.domain.exception.BackendException;
import org.cryptomator.domain.exception.FatalBackendException;
import org.cryptomator.domain.exception.update.GeneralUpdateErrorException;
import org.cryptomator.domain.exception.update.SSLHandshakePreAndroid5UpdateCheckException;
import org.cryptomator.domain.repository.UpdateCheckRepository;
import org.cryptomator.domain.usecases.UpdateCheck;
import org.cryptomator.util.Optional;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@ -163,30 +163,21 @@ public class UpdateCheckRepositoryImpl implements UpdateCheckRepository {
} }
} }
private class LatestVersion { private ECPublicKey getPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
final byte[] publicKey = BaseEncoding //
.base64() //
.decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELOYa5ax7QZvS92HJYCBPBiR2wWfX" + "P9/Oq/yl2J1yg0Vovetp8i1A3yCtoqdHVdVytM1wNV0JXgRbWuNTAr9nlQ==");
private final String version; Key key = KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(publicKey));
private final String urlApk; if (key instanceof ECPublicKey) {
private final String urlReleaseNote; return (ECPublicKey) key;
} else {
LatestVersion(String json) throws GeneralUpdateErrorException { throw new FatalBackendException("Key not an EC public key.");
try {
Claims jws = Jwts //
.parserBuilder().setSigningKey(getPublicKey()) //
.build() //
.parseClaimsJws(json) //
.getBody();
version = jws.get("version", String.class);
urlApk = jws.get("url", String.class);
urlReleaseNote = jws.get("release_notes", String.class);
} catch (Exception e) {
throw new GeneralUpdateErrorException("Failed to parse latest version", e);
}
} }
} }
private static class UpdateCheckImpl implements UpdateCheck { private static class UpdateCheckImpl implements UpdateCheck {
private final String releaseNote; private final String releaseNote;
private final String version; private final String version;
private final String urlApk; private final String urlApk;
@ -227,16 +218,26 @@ public class UpdateCheckRepositoryImpl implements UpdateCheckRepository {
} }
} }
private ECPublicKey getPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException { private class LatestVersion {
final byte[] publicKey = BaseEncoding //
.base64() //
.decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELOYa5ax7QZvS92HJYCBPBiR2wWfX" + "P9/Oq/yl2J1yg0Vovetp8i1A3yCtoqdHVdVytM1wNV0JXgRbWuNTAr9nlQ==");
Key key = KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(publicKey)); private final String version;
if (key instanceof ECPublicKey) { private final String urlApk;
return (ECPublicKey) key; private final String urlReleaseNote;
} else {
throw new FatalBackendException("Key not an EC public key."); LatestVersion(String json) throws GeneralUpdateErrorException {
try {
Claims jws = Jwts //
.parserBuilder().setSigningKey(getPublicKey()) //
.build() //
.parseClaimsJws(json) //
.getBody();
version = jws.get("version", String.class);
urlApk = jws.get("url", String.class);
urlReleaseNote = jws.get("release_notes", String.class);
} catch (Exception e) {
throw new GeneralUpdateErrorException("Failed to parse latest version", e);
}
} }
} }
} }

View File

@ -25,8 +25,9 @@ public class CopyStream {
throw new FatalBackendException(ex); throw new FatalBackendException(ex);
} }
if (count == -1) if (count == -1) {
break; break;
}
try { try {
out.write(copyBuffer, 0, count); out.write(copyBuffer, 0, count);

View File

@ -2,15 +2,15 @@ package org.cryptomator.data.util;
import android.util.Base64; import android.util.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
public class X509CertificateHelper { public class X509CertificateHelper {
private static final String CERT_BEGIN = "-----BEGIN CERTIFICATE-----\n"; private static final String CERT_BEGIN = "-----BEGIN CERTIFICATE-----\n";

View File

@ -32,16 +32,16 @@ class FixedGoogleAccountCredential extends GoogleAccountCredential {
private String accountName; private String accountName;
private FixedGoogleAccountCredential(Context context, String scopesStr) {
super(context, scopesStr);
}
public static FixedGoogleAccountCredential usingOAuth2(Context context, Collection<String> scopes) { public static FixedGoogleAccountCredential usingOAuth2(Context context, Collection<String> scopes) {
Preconditions.checkArgument(scopes != null && scopes.iterator().hasNext()); Preconditions.checkArgument(scopes != null && scopes.iterator().hasNext());
String scopesStr = "oauth2:" + Joiner.on(' ').join(scopes); String scopesStr = "oauth2:" + Joiner.on(' ').join(scopes);
return new FixedGoogleAccountCredential(context, scopesStr); return new FixedGoogleAccountCredential(context, scopesStr);
} }
private FixedGoogleAccountCredential(Context context, String scopesStr) {
super(context, scopesStr);
}
@Override @Override
public void initialize(HttpRequest request) { public void initialize(HttpRequest request) {
FixedRequestHandler handler = new FixedRequestHandler(); FixedRequestHandler handler = new FixedRequestHandler();
@ -81,7 +81,9 @@ class FixedGoogleAccountCredential extends GoogleAccountCredential {
@Beta @Beta
class FixedRequestHandler implements HttpExecuteInterceptor, HttpUnsuccessfulResponseHandler { class FixedRequestHandler implements HttpExecuteInterceptor, HttpUnsuccessfulResponseHandler {
/** Whether we've received a 401 error code indicating the token is invalid. */ /**
* Whether we've received a 401 error code indicating the token is invalid.
*/
boolean received401; boolean received401;
String token; String token;

View File

@ -35,9 +35,7 @@ class GoogleDriveClientFactory {
Logger.getLogger("com.google.api.client").addHandler(new Handler() { Logger.getLogger("com.google.api.client").addHandler(new Handler() {
@Override @Override
public void publish(LogRecord record) { public void publish(LogRecord record) {
if(record.getMessage().startsWith("-------------- RESPONSE --------------") if (record.getMessage().startsWith("-------------- RESPONSE --------------") || record.getMessage().startsWith("-------------- REQUEST --------------") || record.getMessage().startsWith("{\n \"files\": [\n")) {
|| record.getMessage().startsWith("-------------- REQUEST --------------")
|| record.getMessage().startsWith("{\n \"files\": [\n")) {
Timber.tag("GoogleDriveClient").d(record.getMessage()); Timber.tag("GoogleDriveClient").d(record.getMessage());
} }
} }

View File

@ -1,17 +1,16 @@
package org.cryptomator.data.cloud.googledrive; package org.cryptomator.data.cloud.googledrive;
import static org.cryptomator.data.cloud.googledrive.GoogleDriveCloudNodeFactory.from; import android.content.Context;
import static org.cryptomator.data.cloud.googledrive.GoogleDriveCloudNodeFactory.isFolder;
import static org.cryptomator.domain.usecases.cloud.Progress.progress;
import static org.cryptomator.util.file.LruFileCacheUtil.retrieveFromLruCache;
import static org.cryptomator.util.file.LruFileCacheUtil.storeToLruCache;
import static org.cryptomator.util.file.LruFileCacheUtil.Cache.GOOGLE_DRIVE;
import java.io.IOException; import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import java.io.OutputStream; import com.google.api.client.http.HttpResponseException;
import java.util.ArrayList; import com.google.api.services.drive.Drive;
import java.util.Collections; import com.google.api.services.drive.model.About;
import java.util.List; import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import com.google.api.services.drive.model.Revision;
import com.google.api.services.drive.model.RevisionList;
import com.tomclaw.cache.DiskLruCache;
import org.cryptomator.data.util.TransferredBytesAwareOutputStream; import org.cryptomator.data.util.TransferredBytesAwareOutputStream;
import org.cryptomator.domain.CloudNode; import org.cryptomator.domain.CloudNode;
@ -29,20 +28,21 @@ import org.cryptomator.util.Optional;
import org.cryptomator.util.SharedPreferencesHandler; import org.cryptomator.util.SharedPreferencesHandler;
import org.cryptomator.util.file.LruFileCacheUtil; import org.cryptomator.util.file.LruFileCacheUtil;
import com.google.api.client.googleapis.json.GoogleJsonResponseException; import java.io.IOException;
import com.google.api.client.http.HttpResponseException; import java.io.OutputStream;
import com.google.api.services.drive.Drive; import java.util.ArrayList;
import com.google.api.services.drive.model.About; import java.util.Collections;
import com.google.api.services.drive.model.File; import java.util.List;
import com.google.api.services.drive.model.FileList;
import com.google.api.services.drive.model.Revision;
import com.google.api.services.drive.model.RevisionList;
import com.tomclaw.cache.DiskLruCache;
import android.content.Context;
import timber.log.Timber; import timber.log.Timber;
import static org.cryptomator.data.cloud.googledrive.GoogleDriveCloudNodeFactory.from;
import static org.cryptomator.data.cloud.googledrive.GoogleDriveCloudNodeFactory.isFolder;
import static org.cryptomator.domain.usecases.cloud.Progress.progress;
import static org.cryptomator.util.file.LruFileCacheUtil.Cache.GOOGLE_DRIVE;
import static org.cryptomator.util.file.LruFileCacheUtil.retrieveFromLruCache;
import static org.cryptomator.util.file.LruFileCacheUtil.storeToLruCache;
class GoogleDriveImpl { class GoogleDriveImpl {
private static final int STATUS_REQUEST_RANGE_NOT_SATISFIABLE = 416; private static final int STATUS_REQUEST_RANGE_NOT_SATISFIABLE = 416;
@ -284,8 +284,7 @@ class GoogleDriveImpl {
.update( // .update( //
file.getDriveId(), // file.getDriveId(), //
metadata, // metadata, //
in) in).setFields("id,modifiedTime,name,size") //
.setFields("id,modifiedTime,name,size") //
.setSupportsAllDrives(true) // .setSupportsAllDrives(true) //
.execute(); .execute();
} }

View File

@ -3,4 +3,5 @@ package org.cryptomator.data;
import android.app.Application; import android.app.Application;
public class ApplicationStub extends Application { public class ApplicationStub extends Application {
} }

View File

@ -24,6 +24,10 @@ public class CloudFileMatcher extends TypeSafeDiagnosingMatcher<CloudNode> {
Matchers.hasProperty("parent", is(file.getParent()))); Matchers.hasProperty("parent", is(file.getParent())));
} }
public static CloudFileMatcher cloudFile(CloudFile file) {
return new CloudFileMatcher(file);
}
@Override @Override
public void describeTo(Description description) { public void describeTo(Description description) {
delegate.describeTo(description); delegate.describeTo(description);
@ -39,8 +43,4 @@ public class CloudFileMatcher extends TypeSafeDiagnosingMatcher<CloudNode> {
mismatchDescription.appendText("not ").appendDescriptionOf(delegate); mismatchDescription.appendText("not ").appendDescriptionOf(delegate);
return false; return false;
} }
public static CloudFileMatcher cloudFile(CloudFile file) {
return new CloudFileMatcher(file);
}
} }

View File

@ -22,6 +22,10 @@ public class CloudFolderMatcher extends TypeSafeDiagnosingMatcher<CloudNode> {
Matchers.hasProperty("parent", is(folder.getParent()))); Matchers.hasProperty("parent", is(folder.getParent())));
} }
public static CloudFolderMatcher cloudFolder(CloudFolder folder) {
return new CloudFolderMatcher(folder);
}
@Override @Override
public void describeTo(Description description) { public void describeTo(Description description) {
delegate.describeTo(description); delegate.describeTo(description);
@ -37,8 +41,4 @@ public class CloudFolderMatcher extends TypeSafeDiagnosingMatcher<CloudNode> {
mismatchDescription.appendText("not ").appendDescriptionOf(delegate); mismatchDescription.appendText("not ").appendDescriptionOf(delegate);
return false; return false;
} }
public static CloudFolderMatcher cloudFolder(CloudFolder folder) {
return new CloudFolderMatcher(folder);
}
} }

View File

@ -446,8 +446,7 @@ public class CryptoImplVaultFormat7Test {
TestFolder testFile15FolderRename = new TestFolder(aaFolder, shortenedFileNameRename, "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/" + shortenedFileNameRename); TestFolder testFile15FolderRename = new TestFolder(aaFolder, shortenedFileNameRename, "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/" + shortenedFileNameRename);
TestFile testFile15WhatTheHellCloudFileRename = new TestFile(aaFolder, shortenedFileNameRename, "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/" + shortenedFileNameRename, Optional.of(20l), Optional.empty()); TestFile testFile15WhatTheHellCloudFileRename = new TestFile(aaFolder, shortenedFileNameRename, "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/" + shortenedFileNameRename, Optional.of(20l), Optional.empty());
TestFile testFile15ContentFileRename = new TestFile(testFile15FolderRename, "contents.c9r", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/" + shortenedFileNameRename + "/contents.c9r", Optional.of(10l), TestFile testFile15ContentFileRename = new TestFile(testFile15FolderRename, "contents.c9r", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/" + shortenedFileNameRename + "/contents.c9r", Optional.of(10l), Optional.empty());
Optional.empty());
TestFile testFile15NameFileRename = new TestFile(testFile15FolderRename, "name.c9s", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/" + shortenedFileNameRename + "/name.c9s", Optional.of(511l), Optional.empty()); TestFile testFile15NameFileRename = new TestFile(testFile15FolderRename, "name.c9s", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/" + shortenedFileNameRename + "/name.c9s", Optional.of(511l), Optional.empty());
Mockito.when(fileNameCryptor.encryptFilename(BaseEncoding.base64Url(), file15Name + " (1)", dirIdRoot.getBytes())).thenReturn(file15Cipher + "(1)"); Mockito.when(fileNameCryptor.encryptFilename(BaseEncoding.base64Url(), file15Name + " (1)", dirIdRoot.getBytes())).thenReturn(file15Cipher + "(1)");
@ -939,8 +938,7 @@ public class CryptoImplVaultFormat7Test {
CryptoFolder cryptoFolder15 = new CryptoFolder(root, "Directory 15", "/Directory 15/", testDir15DirFile); CryptoFolder cryptoFolder15 = new CryptoFolder(root, "Directory 15", "/Directory 15/", testDir15DirFile);
Mockito.when(cloudContentRepository.file(aaFolder, "dir15.c9r", Optional.ofNullable(null))) Mockito.when(cloudContentRepository.file(aaFolder, "dir15.c9r", Optional.ofNullable(null))).thenReturn(new TestFile(aaFolder, "dir15.c9r", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/dir15.c9r", Optional.empty(), Optional.empty()));
.thenReturn(new TestFile(aaFolder, "dir15.c9r", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/dir15.c9r", Optional.empty(), Optional.empty()));
Mockito.when(cloudContentRepository.folder(rootFolder, "d")).thenReturn(d); Mockito.when(cloudContentRepository.folder(rootFolder, "d")).thenReturn(d);
Mockito.when(cloudContentRepository.folder(d, "11")).thenReturn(bbLvl2Dir); Mockito.when(cloudContentRepository.folder(d, "11")).thenReturn(bbLvl2Dir);
Mockito.when(cloudContentRepository.folder(bbLvl2Dir, "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB")).thenReturn(bbFolder); Mockito.when(cloudContentRepository.folder(bbLvl2Dir, "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB")).thenReturn(bbFolder);
@ -980,8 +978,7 @@ public class CryptoImplVaultFormat7Test {
Mockito.when(fileNameCryptor.encryptFilename(BaseEncoding.base64Url(), dir15Name, dirId1.getBytes())).thenReturn(dir15Cipher); Mockito.when(fileNameCryptor.encryptFilename(BaseEncoding.base64Url(), dir15Name, dirId1.getBytes())).thenReturn(dir15Cipher);
Mockito.when(cloudContentRepository.file(aaFolder, "dir15.c9r", Optional.ofNullable(null))) Mockito.when(cloudContentRepository.file(aaFolder, "dir15.c9r", Optional.ofNullable(null))).thenReturn(new TestFile(aaFolder, "dir15.c9r", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/dir15.c9r", Optional.empty(), Optional.empty()));
.thenReturn(new TestFile(aaFolder, "dir15.c9r", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/dir15.c9r", Optional.empty(), Optional.empty()));
Mockito.when(cloudContentRepository.folder(aaFolder, shortenedFileName)).thenReturn(testDir15); Mockito.when(cloudContentRepository.folder(aaFolder, shortenedFileName)).thenReturn(testDir15);
Mockito.when(cloudContentRepository.folder(rootFolder, "d")).thenReturn(d); Mockito.when(cloudContentRepository.folder(rootFolder, "d")).thenReturn(d);
Mockito.when(cloudContentRepository.folder(d, "11")).thenReturn(bbLvl2Dir); Mockito.when(cloudContentRepository.folder(d, "11")).thenReturn(bbLvl2Dir);
@ -1033,8 +1030,7 @@ public class CryptoImplVaultFormat7Test {
Mockito.when(fileNameCryptor.encryptFilename(BaseEncoding.base64Url(), dir15Name, dirId1.getBytes())).thenReturn(dir15Cipher); Mockito.when(fileNameCryptor.encryptFilename(BaseEncoding.base64Url(), dir15Name, dirId1.getBytes())).thenReturn(dir15Cipher);
Mockito.when(cloudContentRepository.file(aaFolder, "dir15.c9r", Optional.ofNullable(null))) Mockito.when(cloudContentRepository.file(aaFolder, "dir15.c9r", Optional.ofNullable(null))).thenReturn(new TestFile(aaFolder, "dir15.c9r", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/dir15.c9r", Optional.empty(), Optional.empty()));
.thenReturn(new TestFile(aaFolder, "dir15.c9r", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/dir15.c9r", Optional.empty(), Optional.empty()));
Mockito.when(cloudContentRepository.folder(aaFolder, shortenedFileName)).thenReturn(testDir15); Mockito.when(cloudContentRepository.folder(aaFolder, shortenedFileName)).thenReturn(testDir15);
Mockito.when(cloudContentRepository.folder(rootFolder, "d")).thenReturn(d); Mockito.when(cloudContentRepository.folder(rootFolder, "d")).thenReturn(d);
Mockito.when(cloudContentRepository.folder(d, "11")).thenReturn(bbLvl2Dir); Mockito.when(cloudContentRepository.folder(d, "11")).thenReturn(bbLvl2Dir);

View File

@ -860,8 +860,7 @@ class CryptoImplVaultFormatPre7Test {
Mockito.when(fileNameCryptor.encryptFilename(dir15Name, dirId1.getBytes())).thenReturn(dir15Cipher); Mockito.when(fileNameCryptor.encryptFilename(dir15Name, dirId1.getBytes())).thenReturn(dir15Cipher);
Mockito.when(cloudContentRepository.file(aaFolder, "dir15.c9r", Optional.ofNullable(null))) Mockito.when(cloudContentRepository.file(aaFolder, "dir15.c9r", Optional.ofNullable(null))).thenReturn(new TestFile(aaFolder, "dir15.c9r", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/dir15.c9r", Optional.empty(), Optional.empty()));
.thenReturn(new TestFile(aaFolder, "dir15.c9r", "/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/dir15.c9r", Optional.empty(), Optional.empty()));
Mockito.when(dirIdCache.put(Mockito.eq(cryptoFolder1), Mockito.any())).thenReturn(new DirIdCache.DirIdInfo(dirId1, bbFolder)); Mockito.when(dirIdCache.put(Mockito.eq(cryptoFolder1), Mockito.any())).thenReturn(new DirIdCache.DirIdInfo(dirId1, bbFolder));
Mockito.when(dirIdCache.put(Mockito.eq(cryptoFolder15), Mockito.any())).thenReturn(new DirIdCache.DirIdInfo(dirId1, bbFolder)); Mockito.when(dirIdCache.put(Mockito.eq(cryptoFolder15), Mockito.any())).thenReturn(new DirIdCache.DirIdInfo(dirId1, bbFolder));

View File

@ -15,12 +15,15 @@ class RootTestFolder extends TestFolder {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) if (this == o) {
return true; return true;
if (o == null || getClass() != o.getClass()) }
if (o == null || getClass() != o.getClass()) {
return false; return false;
if (!super.equals(o)) }
if (!super.equals(o)) {
return false; return false;
}
RootTestFolder that = (RootTestFolder) o; RootTestFolder that = (RootTestFolder) o;

View File

@ -55,21 +55,27 @@ class TestFile implements CloudFile {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) if (this == o) {
return true; return true;
if (o == null || getClass() != o.getClass()) }
if (o == null || getClass() != o.getClass()) {
return false; return false;
}
TestFile testFile = (TestFile) o; TestFile testFile = (TestFile) o;
if (!Objects.equals(parent, testFile.parent)) if (!Objects.equals(parent, testFile.parent)) {
return false; return false;
if (!Objects.equals(name, testFile.name)) }
if (!Objects.equals(name, testFile.name)) {
return false; return false;
if (!Objects.equals(path, testFile.path)) }
if (!Objects.equals(path, testFile.path)) {
return false; return false;
if (!Objects.equals(size, testFile.size)) }
if (!Objects.equals(size, testFile.size)) {
return false; return false;
}
return Objects.equals(modified, testFile.modified); return Objects.equals(modified, testFile.modified);
} }

View File

@ -24,17 +24,21 @@ class TestFolder implements CloudFolder {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) if (this == o) {
return true; return true;
if (o == null || getClass() != o.getClass()) }
if (o == null || getClass() != o.getClass()) {
return false; return false;
}
TestFolder that = (TestFolder) o; TestFolder that = (TestFolder) o;
if (!Objects.equals(parent, that.parent)) if (!Objects.equals(parent, that.parent)) {
return false; return false;
if (!Objects.equals(name, that.name)) }
if (!Objects.equals(name, that.name)) {
return false; return false;
}
return Objects.equals(path, that.path); return Objects.equals(path, that.path);
} }

View File

@ -49,7 +49,7 @@ public class PropfindResponseParserTest {
private static final String RESPONSE_ONE_FILE_NO_SERVER = "directory-one-file-no-server"; private static final String RESPONSE_ONE_FILE_NO_SERVER = "directory-one-file-no-server";
private static final String RESPONSE_ONE_FILE_AND_FOLDERS = "directory-and-file"; private static final String RESPONSE_ONE_FILE_AND_FOLDERS = "directory-and-file";
private static final String RESPONSE_MAL_FORMATTED_XMLPULLPARSER_EXCEPTION = "malformatted-response-xmlpullparser"; private static final String RESPONSE_MAL_FORMATTED_XMLPULLPARSER_EXCEPTION = "malformatted-response-xmlpullparser";
private final Comparator<PropfindEntryData> ASCENDING_BY_DEPTH = (o1, o2) -> o1.getDepth() - o2.getDepth();
private PropfindResponseParser inTest; private PropfindResponseParser inTest;
@BeforeEach @BeforeEach
@ -144,6 +144,4 @@ public class PropfindResponseParserTest {
} }
return result; return result;
} }
private final Comparator<PropfindEntryData> ASCENDING_BY_DEPTH = (o1, o2) -> o1.getDepth() - o2.getDepth();
} }

View File

@ -14,35 +14,6 @@ public class BackgroundTasks {
private static final ObjectCounts counts = new ObjectCounts(); private static final ObjectCounts counts = new ObjectCounts();
public static class Registration {
private final Class<?> type;
private boolean unregistered = false;
public Registration(Class<?> type) {
this.type = type;
}
public synchronized void unregister() {
if (unregistered) {
return;
}
unregistered = true;
lock.lock();
try {
int count = counts.removeAndGet(type);
Timber.tag("BackgroundTasks").d(caller("unregister", count, counts.count()));
if (count == 0) {
reachedZero.signalAll();
}
} finally {
lock.unlock();
}
}
}
public static void awaitCompleted() { public static void awaitCompleted() {
lock.lock(); lock.lock();
try { try {
@ -90,4 +61,34 @@ public class BackgroundTasks {
.append(caller.getLineNumber()) // .append(caller.getLineNumber()) //
.toString(); .toString();
} }
public static class Registration {
private final Class<?> type;
private boolean unregistered = false;
public Registration(Class<?> type) {
this.type = type;
}
public synchronized void unregister() {
if (unregistered) {
return;
}
unregistered = true;
lock.lock();
try {
int count = counts.removeAndGet(type);
Timber.tag("BackgroundTasks").d(caller("unregister", count, counts.count()));
if (count == 0) {
reachedZero.signalAll();
}
} finally {
lock.unlock();
}
}
}
} }

View File

@ -14,6 +14,17 @@ public class DropboxCloud implements Cloud {
this.username = builder.username; this.username = builder.username;
} }
public static Builder aDropboxCloud() {
return new Builder();
}
public static Builder aCopyOf(DropboxCloud dropboxCloud) {
return new Builder() //
.withId(dropboxCloud.id()) //
.withAccessToken(dropboxCloud.accessToken()) //
.withUsername(dropboxCloud.username());
}
@Override @Override
public Long id() { public Long id() {
return id; return id;
@ -52,23 +63,32 @@ public class DropboxCloud implements Cloud {
return true; return true;
} }
public static Builder aDropboxCloud() {
return new Builder();
}
public static Builder aCopyOf(DropboxCloud dropboxCloud) {
return new Builder() //
.withId(dropboxCloud.id()) //
.withAccessToken(dropboxCloud.accessToken()) //
.withUsername(dropboxCloud.username());
}
@NotNull @NotNull
@Override @Override
public String toString() { public String toString() {
return "DROPBOX"; return "DROPBOX";
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
if (obj == this) {
return true;
}
return internalEquals((DropboxCloud) obj);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
private boolean internalEquals(DropboxCloud obj) {
return id != null && id.equals(obj.id);
}
public static class Builder { public static class Builder {
private Long id; private Long id;
@ -99,22 +119,4 @@ public class DropboxCloud implements Cloud {
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass())
return false;
if (obj == this)
return true;
return internalEquals((DropboxCloud) obj);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
private boolean internalEquals(DropboxCloud obj) {
return id != null && id.equals(obj.id);
}
} }

View File

@ -14,6 +14,17 @@ public class GoogleDriveCloud implements Cloud {
this.username = builder.username; this.username = builder.username;
} }
public static Builder aGoogleDriveCloud() {
return new Builder();
}
public static Builder aCopyOf(GoogleDriveCloud googleDriveCloud) {
return new Builder() //
.withId(googleDriveCloud.id()) //
.withAccessToken(googleDriveCloud.accessToken()) //
.withUsername(googleDriveCloud.username());
}
@Override @Override
public Long id() { public Long id() {
return id; return id;
@ -52,23 +63,32 @@ public class GoogleDriveCloud implements Cloud {
return true; return true;
} }
public static Builder aGoogleDriveCloud() {
return new Builder();
}
public static Builder aCopyOf(GoogleDriveCloud googleDriveCloud) {
return new Builder() //
.withId(googleDriveCloud.id()) //
.withAccessToken(googleDriveCloud.accessToken()) //
.withUsername(googleDriveCloud.username());
}
@NotNull @NotNull
@Override @Override
public String toString() { public String toString() {
return "GOOGLE_DRIVE"; return "GOOGLE_DRIVE";
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
if (obj == this) {
return true;
}
return internalEquals((GoogleDriveCloud) obj);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
private boolean internalEquals(GoogleDriveCloud obj) {
return id != null && id.equals(obj.id);
}
public static class Builder { public static class Builder {
private Long id; private Long id;
@ -99,22 +119,4 @@ public class GoogleDriveCloud implements Cloud {
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass())
return false;
if (obj == this)
return true;
return internalEquals((GoogleDriveCloud) obj);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
private boolean internalEquals(GoogleDriveCloud obj) {
return id != null && id.equals(obj.id);
}
} }

View File

@ -15,6 +15,15 @@ public class LocalStorageCloud implements Cloud {
this.rootUri = builder.rootUri; this.rootUri = builder.rootUri;
} }
public static Builder aLocalStorage() {
return new Builder();
}
public static Builder aCopyOf(LocalStorageCloud localStorageCloud) {
return new Builder() //
.withId(localStorageCloud.id());
}
@Override @Override
public Long id() { public Long id() {
return id; return id;
@ -54,21 +63,32 @@ public class LocalStorageCloud implements Cloud {
return false; return false;
} }
public static Builder aLocalStorage() {
return new Builder();
}
public static Builder aCopyOf(LocalStorageCloud localStorageCloud) {
return new Builder() //
.withId(localStorageCloud.id());
}
@NotNull @NotNull
@Override @Override
public String toString() { public String toString() {
return "LOCAL"; return "LOCAL";
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
if (obj == this) {
return true;
}
return internalEquals((LocalStorageCloud) obj);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
private boolean internalEquals(LocalStorageCloud obj) {
return id != null && id.equals(obj.id);
}
public static class Builder { public static class Builder {
private Long id; private Long id;
@ -97,22 +117,4 @@ public class LocalStorageCloud implements Cloud {
return "LOCAL"; return "LOCAL";
} }
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass())
return false;
if (obj == this)
return true;
return internalEquals((LocalStorageCloud) obj);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
private boolean internalEquals(LocalStorageCloud obj) {
return id != null && id.equals(obj.id);
}
} }

View File

@ -14,6 +14,17 @@ public class OnedriveCloud implements Cloud {
this.username = builder.username; this.username = builder.username;
} }
public static Builder aOnedriveCloud() {
return new Builder();
}
public static Builder aCopyOf(OnedriveCloud oneDriveCloud) {
return new Builder() //
.withId(oneDriveCloud.id()) //
.withAccessToken(oneDriveCloud.accessToken()) //
.withUsername(oneDriveCloud.username());
}
@Override @Override
public Long id() { public Long id() {
return id; return id;
@ -58,15 +69,24 @@ public class OnedriveCloud implements Cloud {
return "ONEDRIVE"; return "ONEDRIVE";
} }
public static Builder aOnedriveCloud() { @Override
return new Builder(); public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
if (obj == this) {
return true;
}
return internalEquals((OnedriveCloud) obj);
} }
public static Builder aCopyOf(OnedriveCloud oneDriveCloud) { @Override
return new Builder() // public int hashCode() {
.withId(oneDriveCloud.id()) // return id == null ? 0 : id.hashCode();
.withAccessToken(oneDriveCloud.accessToken()) // }
.withUsername(oneDriveCloud.username());
private boolean internalEquals(OnedriveCloud obj) {
return id != null && id.equals(obj.id);
} }
public static class Builder { public static class Builder {
@ -99,22 +119,4 @@ public class OnedriveCloud implements Cloud {
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass())
return false;
if (obj == this)
return true;
return internalEquals((OnedriveCloud) obj);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
private boolean internalEquals(OnedriveCloud obj) {
return id != null && id.equals(obj.id);
}
} }

View File

@ -5,24 +5,6 @@ import java.io.Serializable;
public class Vault implements Serializable { public class Vault implements Serializable {
private static final Long NOT_SET = Long.MIN_VALUE; private static final Long NOT_SET = Long.MIN_VALUE;
public static Builder aVault() {
return new Builder();
}
public static Builder aCopyOf(Vault vault) {
return new Builder() //
.withId(vault.getId()) //
.withCloud(vault.getCloud()) //
.withCloudType(vault.getCloudType()) //
.withName(vault.getName()) //
.withPath(vault.getPath()) //
.withUnlocked(vault.isUnlocked()) //
.withSavedPassword(vault.getPassword()) //
.withVersion(vault.getVersion()) //
.withPosition(vault.getPosition());
}
private final Long id; private final Long id;
private final String name; private final String name;
private final String path; private final String path;
@ -45,6 +27,23 @@ public class Vault implements Serializable {
this.position = builder.position; this.position = builder.position;
} }
public static Builder aVault() {
return new Builder();
}
public static Builder aCopyOf(Vault vault) {
return new Builder() //
.withId(vault.getId()) //
.withCloud(vault.getCloud()) //
.withCloudType(vault.getCloudType()) //
.withName(vault.getName()) //
.withPath(vault.getPath()) //
.withUnlocked(vault.isUnlocked()) //
.withSavedPassword(vault.getPassword()) //
.withVersion(vault.getVersion()) //
.withPosition(vault.getPosition());
}
public Long getId() { public Long getId() {
return id; return id;
} }
@ -81,6 +80,26 @@ public class Vault implements Serializable {
return position; return position;
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
if (obj == this) {
return true;
}
return internalEquals((Vault) obj);
}
private boolean internalEquals(Vault obj) {
return id != null && id.equals(obj.id);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
public static class Builder { public static class Builder {
private Long id = NOT_SET; private Long id = NOT_SET;
@ -190,22 +209,4 @@ public class Vault implements Serializable {
} }
} }
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass())
return false;
if (obj == this)
return true;
return internalEquals((Vault) obj);
}
private boolean internalEquals(Vault obj) {
return id != null && id.equals(obj.id);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
} }

View File

@ -18,6 +18,19 @@ public class WebDavCloud implements Cloud {
this.certificate = builder.certificate; this.certificate = builder.certificate;
} }
public static Builder aWebDavCloudCloud() {
return new Builder();
}
public static Builder aCopyOf(WebDavCloud webDavCloud) {
return new Builder() //
.withId(webDavCloud.id()) //
.withUrl(webDavCloud.url()) //
.withUsername(webDavCloud.username()) //
.withPassword(webDavCloud.password()) //
.withCertificate(webDavCloud.certificate());
}
@Override @Override
public Long id() { public Long id() {
return id; return id;
@ -68,25 +81,32 @@ public class WebDavCloud implements Cloud {
return true; return true;
} }
public static Builder aWebDavCloudCloud() {
return new Builder();
}
public static Builder aCopyOf(WebDavCloud webDavCloud) {
return new Builder() //
.withId(webDavCloud.id()) //
.withUrl(webDavCloud.url()) //
.withUsername(webDavCloud.username()) //
.withPassword(webDavCloud.password()) //
.withCertificate(webDavCloud.certificate());
}
@NotNull @NotNull
@Override @Override
public String toString() { public String toString() {
return "WEBDAV"; return "WEBDAV";
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
if (obj == this) {
return true;
}
return internalEquals((WebDavCloud) obj);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
private boolean internalEquals(WebDavCloud obj) {
return id != null && id.equals(obj.id);
}
public static class Builder { public static class Builder {
private Long id; private Long id;
@ -129,22 +149,4 @@ public class WebDavCloud implements Cloud {
} }
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass())
return false;
if (obj == this)
return true;
return internalEquals((WebDavCloud) obj);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
private boolean internalEquals(WebDavCloud obj) {
return id != null && id.equals(obj.id);
}
} }

View File

@ -4,4 +4,5 @@ import javax.inject.Scope;
@Scope @Scope
public @interface PerView { public @interface PerView {
} }

View File

@ -1,4 +1,5 @@
package org.cryptomator.domain.exception; package org.cryptomator.domain.exception;
public class AlreadyExistException extends BackendException { public class AlreadyExistException extends BackendException {
} }

View File

@ -1,4 +1,5 @@
package org.cryptomator.domain.exception; package org.cryptomator.domain.exception;
public class ForbiddenException extends BackendException { public class ForbiddenException extends BackendException {
} }

View File

@ -1,4 +1,5 @@
package org.cryptomator.domain.exception; package org.cryptomator.domain.exception;
public class NotImplementedException extends BackendException { public class NotImplementedException extends BackendException {
} }

View File

@ -1,4 +1,5 @@
package org.cryptomator.domain.exception; package org.cryptomator.domain.exception;
public class ParentFolderDoesNotExistException extends BackendException { public class ParentFolderDoesNotExistException extends BackendException {
} }

View File

@ -1,4 +1,5 @@
package org.cryptomator.domain.exception; package org.cryptomator.domain.exception;
public class ServerNotWebdavCompatibleException extends BackendException { public class ServerNotWebdavCompatibleException extends BackendException {
} }

View File

@ -1,4 +1,5 @@
package org.cryptomator.domain.exception; package org.cryptomator.domain.exception;
public class TypeMismatchException extends BackendException { public class TypeMismatchException extends BackendException {
} }

View File

@ -1,4 +1,5 @@
package org.cryptomator.domain.exception; package org.cryptomator.domain.exception;
public class UnauthorizedException extends BackendException { public class UnauthorizedException extends BackendException {
} }

View File

@ -3,6 +3,7 @@ package org.cryptomator.domain.exception.authentication;
import org.cryptomator.domain.Cloud; import org.cryptomator.domain.Cloud;
public class WebDavNotSupportedException extends AuthenticationException { public class WebDavNotSupportedException extends AuthenticationException {
public WebDavNotSupportedException(Cloud cloud) { public WebDavNotSupportedException(Cloud cloud) {
super(cloud, "WebDav not supported by server"); super(cloud, "WebDav not supported by server");
} }

View File

@ -3,6 +3,7 @@ package org.cryptomator.domain.exception.authentication;
import org.cryptomator.domain.Cloud; import org.cryptomator.domain.Cloud;
public class WebDavServerNotFoundException extends AuthenticationException { public class WebDavServerNotFoundException extends AuthenticationException {
public WebDavServerNotFoundException(Cloud cloud) { public WebDavServerNotFoundException(Cloud cloud) {
super(cloud); super(cloud);
} }

View File

@ -6,7 +6,6 @@ import io.reactivex.Scheduler;
* Thread abstraction created to change the execution context from any thread to any other thread. * Thread abstraction created to change the execution context from any thread to any other thread.
* Useful to encapsulate a UI Thread for example, since some job will be done in background, an * Useful to encapsulate a UI Thread for example, since some job will be done in background, an
* implementation of this interface will change context and update the UI. * implementation of this interface will change context and update the UI.
*
*/ */
public interface PostExecutionThread { public interface PostExecutionThread {

View File

@ -5,7 +5,7 @@ import java.util.concurrent.Executor;
/** /**
* Executor implementation can be based on different frameworks or techniques of asynchronous * Executor implementation can be based on different frameworks or techniques of asynchronous
* execution, but every implementation will execute a use case out of the UI thread. * execution, but every implementation will execute a use case out of the UI thread.
*
*/ */
public interface ThreadExecutor extends Executor { public interface ThreadExecutor extends Executor {
} }

View File

@ -43,21 +43,18 @@ public interface CloudContentRepository<CloudType extends Cloud, NodeType extend
* Creates a cloud folder and maybe intermediate directories. * Creates a cloud folder and maybe intermediate directories.
* *
* @return created cloud folder (migth be different from target) * @return created cloud folder (migth be different from target)
*
* @throws org.cryptomator.domain.exception.CloudNodeAlreadyExistsException If a cloud node with the same folder name already exists * @throws org.cryptomator.domain.exception.CloudNodeAlreadyExistsException If a cloud node with the same folder name already exists
*/ */
DirType create(DirType folder) throws BackendException; DirType create(DirType folder) throws BackendException;
/** /**
* @return moved cloud folder (might be different from target) * @return moved cloud folder (might be different from target)
*
* @throws org.cryptomator.domain.exception.CloudNodeAlreadyExistsException If a cloud node with the same target name already exists * @throws org.cryptomator.domain.exception.CloudNodeAlreadyExistsException If a cloud node with the same target name already exists
*/ */
DirType move(DirType source, DirType target) throws BackendException; DirType move(DirType source, DirType target) throws BackendException;
/** /**
* @return moved cloud file (might be different from target) * @return moved cloud file (might be different from target)
*
* @throws org.cryptomator.domain.exception.CloudNodeAlreadyExistsException If a cloud node with the same target name already exists * @throws org.cryptomator.domain.exception.CloudNodeAlreadyExistsException If a cloud node with the same target name already exists
*/ */
FileType move(FileType source, FileType target) throws BackendException; FileType move(FileType source, FileType target) throws BackendException;

View File

@ -1,12 +1,12 @@
package org.cryptomator.domain.repository; package org.cryptomator.domain.repository;
import java.io.File;
import org.cryptomator.domain.exception.BackendException; import org.cryptomator.domain.exception.BackendException;
import org.cryptomator.domain.exception.update.GeneralUpdateErrorException; import org.cryptomator.domain.exception.update.GeneralUpdateErrorException;
import org.cryptomator.domain.usecases.UpdateCheck; import org.cryptomator.domain.usecases.UpdateCheck;
import org.cryptomator.util.Optional; import org.cryptomator.util.Optional;
import java.io.File;
public interface UpdateCheckRepository { public interface UpdateCheckRepository {
Optional<UpdateCheck> getUpdateCheck(String version) throws BackendException; Optional<UpdateCheck> getUpdateCheck(String version) throws BackendException;

View File

@ -1,11 +1,6 @@
package org.cryptomator.domain.usecases; package org.cryptomator.domain.usecases;
import java.security.Key; import com.google.common.io.BaseEncoding;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import org.cryptomator.domain.exception.BackendException; import org.cryptomator.domain.exception.BackendException;
import org.cryptomator.domain.exception.FatalBackendException; import org.cryptomator.domain.exception.FatalBackendException;
@ -15,7 +10,12 @@ import org.cryptomator.domain.repository.UpdateCheckRepository;
import org.cryptomator.generator.Parameter; import org.cryptomator.generator.Parameter;
import org.cryptomator.generator.UseCase; import org.cryptomator.generator.UseCase;
import com.google.common.io.BaseEncoding; import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException; import io.jsonwebtoken.JwtException;

View File

@ -1,12 +1,12 @@
package org.cryptomator.domain.usecases; package org.cryptomator.domain.usecases;
import java.io.File;
import org.cryptomator.domain.exception.BackendException; import org.cryptomator.domain.exception.BackendException;
import org.cryptomator.domain.repository.UpdateCheckRepository; import org.cryptomator.domain.repository.UpdateCheckRepository;
import org.cryptomator.generator.Parameter; import org.cryptomator.generator.Parameter;
import org.cryptomator.generator.UseCase; import org.cryptomator.generator.UseCase;
import java.io.File;
@UseCase @UseCase
public class DoUpdate { public class DoUpdate {

View File

@ -1,6 +1,7 @@
package org.cryptomator.domain.usecases; package org.cryptomator.domain.usecases;
public abstract class NoOpResultHandler<T> implements ResultHandler<T> { public abstract class NoOpResultHandler<T> implements ResultHandler<T> {
@Override @Override
public void onFinished() { public void onFinished() {
// no-op // no-op

View File

@ -8,28 +8,6 @@ import org.cryptomator.domain.usecases.cloud.ProgressState;
*/ */
public abstract class ProgressAwareResultHandler<T, S extends ProgressState> implements ResultHandler<T>, ProgressAware<S> { public abstract class ProgressAwareResultHandler<T, S extends ProgressState> implements ResultHandler<T>, ProgressAware<S> {
public static class NoOp<T, S extends ProgressState> extends ProgressAwareResultHandler<T, S> {
@Override
public void onSuccess(T result) {
// no-op
}
@Override
public void onError(Throwable e) {
// no-op
}
@Override
public void onFinished() {
// no-op
}
@Override
public void onProgress(Progress<S> progress) {
// no-op
}
}
public static <T, S extends ProgressState> ProgressAwareResultHandler<T, S> from(final ResultHandler<T> resultHandler) { public static <T, S extends ProgressState> ProgressAwareResultHandler<T, S> from(final ResultHandler<T> resultHandler) {
return new ProgressAwareResultHandler<T, S>() { return new ProgressAwareResultHandler<T, S>() {
@Override @Override
@ -78,4 +56,27 @@ public abstract class ProgressAwareResultHandler<T, S extends ProgressState> imp
}; };
} }
public static class NoOp<T, S extends ProgressState> extends ProgressAwareResultHandler<T, S> {
@Override
public void onSuccess(T result) {
// no-op
}
@Override
public void onError(Throwable e) {
// no-op
}
@Override
public void onFinished() {
// no-op
}
@Override
public void onProgress(Progress<S> progress) {
// no-op
}
}
} }

View File

@ -5,6 +5,14 @@ import org.cryptomator.domain.usecases.cloud.ProgressState;
public class ResultWithProgress<T, S extends ProgressState> { public class ResultWithProgress<T, S extends ProgressState> {
private final Progress<S> progress;
private final T value;
private ResultWithProgress(T value, Progress<S> progress) {
this.value = value;
this.progress = progress;
}
public static <T, S extends ProgressState> ResultWithProgress<T, S> progress(Progress<S> progress) { public static <T, S extends ProgressState> ResultWithProgress<T, S> progress(Progress<S> progress) {
return new ResultWithProgress<>(null, progress); return new ResultWithProgress<>(null, progress);
} }
@ -17,14 +25,6 @@ public class ResultWithProgress<T, S extends ProgressState> {
return new ResultWithProgress<>(null, Progress.started(state)); return new ResultWithProgress<>(null, Progress.started(state));
} }
private final Progress<S> progress;
private final T value;
private ResultWithProgress(T value, Progress<S> progress) {
this.value = value;
this.progress = progress;
}
public Progress<S> progress() { public Progress<S> progress() {
return progress; return progress;
} }

View File

@ -10,16 +10,16 @@ import java.io.InputStream;
public class ByteArrayDataSource implements DataSource { public class ByteArrayDataSource implements DataSource {
public static DataSource from(byte[] bytes) {
return new ByteArrayDataSource(bytes);
}
private final byte[] bytes; private final byte[] bytes;
private ByteArrayDataSource(byte[] bytes) { private ByteArrayDataSource(byte[] bytes) {
this.bytes = bytes; this.bytes = bytes;
} }
public static DataSource from(byte[] bytes) {
return new ByteArrayDataSource(bytes);
}
@Override @Override
public Optional<Long> size(Context context) { public Optional<Long> size(Context context) {
long size = bytes.length; long size = bytes.length;

View File

@ -10,10 +10,6 @@ import java.io.InputStream;
public class CancelAwareDataSource implements DataSource { public class CancelAwareDataSource implements DataSource {
public static CancelAwareDataSource wrap(DataSource delegate, Flag cancelled) {
return new CancelAwareDataSource(delegate, cancelled);
}
private final DataSource delegate; private final DataSource delegate;
private final Flag cancelled; private final Flag cancelled;
@ -22,6 +18,10 @@ public class CancelAwareDataSource implements DataSource {
this.cancelled = cancelled; this.cancelled = cancelled;
} }
public static CancelAwareDataSource wrap(DataSource delegate, Flag cancelled) {
return new CancelAwareDataSource(delegate, cancelled);
}
@Override @Override
public Optional<Long> size(Context context) { public Optional<Long> size(Context context) {
if (cancelled.get()) { if (cancelled.get()) {

View File

@ -1,18 +1,14 @@
package org.cryptomator.domain.usecases.cloud; package org.cryptomator.domain.usecases.cloud;
import androidx.annotation.NonNull;
import org.cryptomator.domain.exception.CancellationException; import org.cryptomator.domain.exception.CancellationException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import androidx.annotation.NonNull;
class CancelAwareInputStream extends InputStream { class CancelAwareInputStream extends InputStream {
public static CancelAwareInputStream wrap(InputStream delegate, Flag cancelled) {
return new CancelAwareInputStream(delegate, cancelled);
}
private final InputStream delegate; private final InputStream delegate;
private final Flag cancelled; private final Flag cancelled;
@ -21,6 +17,10 @@ class CancelAwareInputStream extends InputStream {
this.cancelled = cancelled; this.cancelled = cancelled;
} }
public static CancelAwareInputStream wrap(InputStream delegate, Flag cancelled) {
return new CancelAwareInputStream(delegate, cancelled);
}
@Override @Override
public int read() throws IOException { public int read() throws IOException {
if (cancelled.get()) { if (cancelled.get()) {

View File

@ -7,6 +7,11 @@ public class DownloadState implements FileTransferState {
private final CloudFile file; private final CloudFile file;
private final boolean download; private final boolean download;
private DownloadState(CloudFile file, boolean download) {
this.download = download;
this.file = file;
}
public static DownloadState download(CloudFile file) { public static DownloadState download(CloudFile file) {
return new DownloadState(file, true); return new DownloadState(file, true);
} }
@ -15,11 +20,6 @@ public class DownloadState implements FileTransferState {
return new DownloadState(file, false); return new DownloadState(file, false);
} }
private DownloadState(CloudFile file, boolean download) {
this.download = download;
this.file = file;
}
@Override @Override
public CloudFile file() { public CloudFile file() {
return file; return file;

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