E2E overhaul
- Limit online editing/stream when e2e-encrypted file - download files in subfolder to correct local folder name - use httpd/unix-directory for folder in metadata - set encryption status of subfolder Signed-off-by: nextcloud-android-bot <android@nextcloud.com> Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
parent
4f6e810226
commit
a2fc279c01
|
@ -126,6 +126,8 @@ services:
|
|||
- su www-data -c "php /var/www/html/occ config:app:set circles --value 1 local_is_non_ssl"
|
||||
- su www-data -c "php /var/www/html/occ config:system:set allow_local_remote_servers --value true --type bool"
|
||||
- su www-data -c "php /var/www/html/occ circles:manage:create test public publicCircle"
|
||||
- su www-data -c "git clone -b master https://github.com/nextcloud/end_to_end_encryption/ /var/www/html/apps/end_to_end_encryption/"
|
||||
- su www-data -c "php /var/www/html/occ app:enable end_to_end_encryption"
|
||||
- /usr/local/bin/run.sh
|
||||
|
||||
trigger:
|
||||
|
|
|
@ -397,6 +397,7 @@ dependencies {
|
|||
implementation "com.github.stateless4j:stateless4j:2.6.0"
|
||||
androidTestImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
||||
androidTestImplementation "org.mockito:mockito-android:3.3.3"
|
||||
androidTestImplementation 'net.bytebuddy:byte-buddy:1.10.5'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
|
|
|
@ -1 +1 @@
|
|||
372
|
||||
372
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<Match>
|
||||
<Or>
|
||||
<Class name="~.*BindingImpl"/>
|
||||
<Class name="~.*\.DataBinderMapperImpl"/>
|
||||
<Class name="~.*\.DataBinderMapperImpl" />
|
||||
</Or>
|
||||
</Match>
|
||||
|
||||
|
@ -53,6 +53,7 @@
|
|||
<Bug pattern="PATH_TRAVERSAL_IN" />
|
||||
<Bug pattern="ANDROID_EXTERNAL_FILE_ACCESS" />
|
||||
<Bug pattern="BAS_BLOATED_ASSIGNMENT_SCOPE" />
|
||||
<Bug pattern="IMC_IMMATURE_CLASS_BAD_SERIALVERSIONUID" />
|
||||
|
||||
<!-- This is unmanageable for now due to large amount of interconnected static state -->
|
||||
<Bug pattern="FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY" />
|
||||
|
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2020 Tobias Kaminsky
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client;
|
||||
|
||||
import android.accounts.AccountManager;
|
||||
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.lib.resources.e2ee.ToggleEncryptionRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.users.GetPrivateKeyOperation;
|
||||
import com.owncloud.android.lib.resources.users.GetPublicKeyOperation;
|
||||
import com.owncloud.android.lib.resources.users.SendCSROperation;
|
||||
import com.owncloud.android.lib.resources.users.StorePrivateKeyOperation;
|
||||
import com.owncloud.android.operations.RemoveFileOperation;
|
||||
import com.owncloud.android.utils.CsrHelper;
|
||||
import com.owncloud.android.utils.EncryptionUtils;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
|
||||
import net.bytebuddy.utility.RandomString;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
public class EndToEndRandomIT extends AbstractIT {
|
||||
public enum Action {
|
||||
CREATE_FOLDER,
|
||||
GO_INTO_FOLDER,
|
||||
GO_UP,
|
||||
UPLOAD_FILE
|
||||
}
|
||||
|
||||
private static ArbitraryDataProvider arbitraryDataProvider;
|
||||
|
||||
private OCFile currentFolder;
|
||||
private int actionCount = 20;
|
||||
private String rootEncFolder = "/e/";
|
||||
|
||||
@BeforeClass
|
||||
public static void initClass() {
|
||||
arbitraryDataProvider = new ArbitraryDataProvider(targetContext.getContentResolver());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void run() throws Exception {
|
||||
init();
|
||||
|
||||
for (int i = 0; i < actionCount; i++) {
|
||||
Action nextAction = Action.values()[new Random().nextInt(Action.values().length)];
|
||||
|
||||
switch (nextAction) {
|
||||
case CREATE_FOLDER:
|
||||
createFolder(i);
|
||||
break;
|
||||
|
||||
case GO_INTO_FOLDER:
|
||||
goIntoFolder(i);
|
||||
break;
|
||||
|
||||
case GO_UP:
|
||||
goUp(i);
|
||||
break;
|
||||
|
||||
case UPLOAD_FILE:
|
||||
uploadFile(i);
|
||||
break;
|
||||
|
||||
default:
|
||||
Log_OC.d(this, "[" + i + "/" + actionCount + "]" + " Unknown action: " + nextAction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uploadOneFile() throws Exception {
|
||||
init();
|
||||
|
||||
uploadFile(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFolder() throws Exception {
|
||||
init();
|
||||
|
||||
currentFolder = createFolder(0);
|
||||
assertNotNull(currentFolder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSubFolders() throws Exception {
|
||||
init();
|
||||
|
||||
currentFolder = createFolder(0);
|
||||
assertNotNull(currentFolder);
|
||||
|
||||
currentFolder = createFolder(1);
|
||||
assertNotNull(currentFolder);
|
||||
|
||||
currentFolder = createFolder(2);
|
||||
assertNotNull(currentFolder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSubFoldersWithFiles() throws Exception {
|
||||
init();
|
||||
|
||||
currentFolder = createFolder(0);
|
||||
assertNotNull(currentFolder);
|
||||
|
||||
uploadFile(1);
|
||||
uploadFile(1);
|
||||
uploadFile(2);
|
||||
|
||||
currentFolder = createFolder(1);
|
||||
assertNotNull(currentFolder);
|
||||
uploadFile(11);
|
||||
uploadFile(12);
|
||||
uploadFile(13);
|
||||
|
||||
currentFolder = createFolder(2);
|
||||
assertNotNull(currentFolder);
|
||||
|
||||
uploadFile(21);
|
||||
uploadFile(22);
|
||||
uploadFile(23);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pseudoRandom() throws Exception {
|
||||
init();
|
||||
|
||||
uploadFile(1);
|
||||
createFolder(2);
|
||||
goIntoFolder(3);
|
||||
goUp(4);
|
||||
createFolder(5);
|
||||
uploadFile(6);
|
||||
goUp(7);
|
||||
goIntoFolder(8);
|
||||
goIntoFolder(9);
|
||||
uploadFile(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteFile() throws Exception {
|
||||
init();
|
||||
|
||||
uploadFile(1);
|
||||
deleteFile(1);
|
||||
}
|
||||
|
||||
private void init() throws Exception {
|
||||
// create folder
|
||||
createFolder(rootEncFolder);
|
||||
OCFile encFolder = createFolder(rootEncFolder + RandomString.make(5) + "/");
|
||||
|
||||
// encrypt it
|
||||
assertTrue(new ToggleEncryptionRemoteOperation(encFolder.getLocalId(),
|
||||
encFolder.getRemotePath(),
|
||||
true)
|
||||
.execute(client).isSuccess());
|
||||
encFolder.setEncrypted(true);
|
||||
getStorageManager().saveFolder(encFolder, new ArrayList<>(), new ArrayList<>());
|
||||
|
||||
useExistingKeys();
|
||||
|
||||
rootEncFolder = encFolder.getDecryptedRemotePath();
|
||||
currentFolder = encFolder;
|
||||
}
|
||||
|
||||
private OCFile createFolder(int i) {
|
||||
String path = currentFolder.getDecryptedRemotePath() + RandomString.make(5) + "/";
|
||||
Log_OC.d(this, "[" + i + "/" + actionCount + "] " + "Create folder: " + path);
|
||||
|
||||
return createFolder(path);
|
||||
}
|
||||
|
||||
private void goIntoFolder(int i) {
|
||||
ArrayList<OCFile> folders = new ArrayList<>();
|
||||
for (OCFile file : getStorageManager().getFolderContent(currentFolder, false)) {
|
||||
if (file.isFolder()) {
|
||||
folders.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (folders.isEmpty()) {
|
||||
Log_OC.d(this, "[" + i + "/" + actionCount + "] " + "Go into folder: No folders");
|
||||
return;
|
||||
}
|
||||
|
||||
currentFolder = folders.get(new Random().nextInt(folders.size()));
|
||||
Log_OC.d(this,
|
||||
"[" + i + "/" + actionCount + "] " + "Go into folder: " + currentFolder.getDecryptedRemotePath());
|
||||
}
|
||||
|
||||
private void goUp(int i) {
|
||||
if (currentFolder.getRemotePath().equals(rootEncFolder)) {
|
||||
Log_OC.d(this,
|
||||
"[" + i + "/" + actionCount + "] " + "Go up to folder: " + currentFolder.getDecryptedRemotePath());
|
||||
return;
|
||||
}
|
||||
|
||||
currentFolder = getStorageManager().getFileById(currentFolder.getParentId());
|
||||
if (currentFolder == null) {
|
||||
throw new RuntimeException("Current folder is null");
|
||||
}
|
||||
|
||||
Log_OC.d(this,
|
||||
"[" + i + "/" + actionCount + "] " + "Go up to folder: " + currentFolder.getDecryptedRemotePath());
|
||||
}
|
||||
|
||||
private void uploadFile(int i) throws IOException {
|
||||
String fileName = RandomString.make(5) + ".txt";
|
||||
createFile(fileName, new Random().nextInt(50000));
|
||||
String path = currentFolder.getRemotePath() + fileName;
|
||||
|
||||
Log_OC.d(this,
|
||||
"[" + i + "/" + actionCount + "] " +
|
||||
"Upload file to: " + currentFolder.getDecryptedRemotePath() + fileName);
|
||||
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + File.separator + fileName,
|
||||
path,
|
||||
account.name);
|
||||
uploadOCUpload(ocUpload);
|
||||
}
|
||||
|
||||
private void deleteFile(int i) {
|
||||
ArrayList<OCFile> files = new ArrayList<>();
|
||||
for (OCFile file : getStorageManager().getFolderContent(currentFolder, false)) {
|
||||
if (!file.isFolder()) {
|
||||
files.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
OCFile fileToDelete = files.get(new Random().nextInt(files.size()));
|
||||
|
||||
Log_OC.d(this,
|
||||
"[" + i + "/" + actionCount + "] " +
|
||||
"Delete file: " + currentFolder.getDecryptedRemotePath() + fileToDelete.getDecryptedFileName());
|
||||
|
||||
assertTrue(new RemoveFileOperation(fileToDelete,
|
||||
false,
|
||||
account,
|
||||
false,
|
||||
targetContext)
|
||||
.execute(client, getStorageManager())
|
||||
.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reInit() throws Exception {
|
||||
// create folder
|
||||
OCFile encFolder = createFolder(rootEncFolder);
|
||||
|
||||
// encrypt it
|
||||
assertTrue(new ToggleEncryptionRemoteOperation(encFolder.getLocalId(),
|
||||
encFolder.getRemotePath(),
|
||||
true)
|
||||
.execute(client).isSuccess());
|
||||
encFolder.setEncrypted(true);
|
||||
getStorageManager().saveFolder(encFolder, new ArrayList<>(), new ArrayList<>());
|
||||
|
||||
createKeys();
|
||||
|
||||
// delete keys
|
||||
arbitraryDataProvider.deleteKeyForAccount(account.name, EncryptionUtils.PRIVATE_KEY);
|
||||
arbitraryDataProvider.deleteKeyForAccount(account.name, EncryptionUtils.PUBLIC_KEY);
|
||||
arbitraryDataProvider.deleteKeyForAccount(account.name, EncryptionUtils.MNEMONIC);
|
||||
|
||||
useExistingKeys();
|
||||
}
|
||||
|
||||
private void useExistingKeys() throws Exception {
|
||||
// download them from server
|
||||
GetPublicKeyOperation publicKeyOperation = new GetPublicKeyOperation();
|
||||
RemoteOperationResult publicKeyResult = publicKeyOperation.execute(account, targetContext);
|
||||
|
||||
assertTrue(publicKeyResult.isSuccess());
|
||||
|
||||
String publicKeyFromServer = (String) publicKeyResult.getData().get(0);
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name,
|
||||
EncryptionUtils.PUBLIC_KEY,
|
||||
publicKeyFromServer);
|
||||
|
||||
GetPrivateKeyOperation privateKeyOperation = new GetPrivateKeyOperation();
|
||||
RemoteOperationResult privateKeyResult = privateKeyOperation.execute(account, targetContext);
|
||||
assertTrue(privateKeyResult.isSuccess());
|
||||
|
||||
String privateKey = (String) privateKeyResult.getData().get(0);
|
||||
|
||||
String mnemonic = generateMnemonicString();
|
||||
String decryptedPrivateKey = EncryptionUtils.decryptPrivateKey(privateKey, mnemonic);
|
||||
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name,
|
||||
EncryptionUtils.PRIVATE_KEY, decryptedPrivateKey);
|
||||
|
||||
Log_OC.d(this, "Private key successfully decrypted and stored");
|
||||
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.MNEMONIC, mnemonic);
|
||||
}
|
||||
|
||||
/*
|
||||
TODO do not c&p code
|
||||
*/
|
||||
private void createKeys() throws Exception {
|
||||
String publicKey;
|
||||
|
||||
// Create public/private key pair
|
||||
KeyPair keyPair = EncryptionUtils.generateKeyPair();
|
||||
|
||||
// create CSR
|
||||
AccountManager accountManager = AccountManager.get(targetContext);
|
||||
String userId = accountManager.getUserData(account, AccountUtils.Constants.KEY_USER_ID);
|
||||
String urlEncoded = CsrHelper.generateCsrPemEncodedString(keyPair, userId);
|
||||
|
||||
SendCSROperation operation = new SendCSROperation(urlEncoded);
|
||||
RemoteOperationResult result = operation.execute(account, targetContext);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
publicKey = (String) result.getData().get(0);
|
||||
} else {
|
||||
throw new Exception("failed to send CSR", result.getException());
|
||||
}
|
||||
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
String privateKeyString = EncryptionUtils.encodeBytesToBase64String(privateKey.getEncoded());
|
||||
String privatePemKeyString = EncryptionUtils.privateKeyToPEM(privateKey);
|
||||
String encryptedPrivateKey = EncryptionUtils.encryptPrivateKey(privatePemKeyString,
|
||||
generateMnemonicString());
|
||||
|
||||
// upload encryptedPrivateKey
|
||||
StorePrivateKeyOperation storePrivateKeyOperation = new StorePrivateKeyOperation(encryptedPrivateKey);
|
||||
RemoteOperationResult storePrivateKeyResult = storePrivateKeyOperation.execute(account, targetContext);
|
||||
|
||||
if (storePrivateKeyResult.isSuccess()) {
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.PRIVATE_KEY,
|
||||
privateKeyString);
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.PUBLIC_KEY, publicKey);
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.MNEMONIC,
|
||||
generateMnemonicString());
|
||||
} else {
|
||||
throw new RuntimeException("Error uploading private key!");
|
||||
}
|
||||
}
|
||||
|
||||
private String generateMnemonicString() {
|
||||
return "1 2 3 4 5 6";
|
||||
}
|
||||
}
|
|
@ -163,11 +163,12 @@ public class FileDisplayActivityIT extends AbstractIT {
|
|||
|
||||
@Test
|
||||
public void allFiles() {
|
||||
// ActivityScenario<FileDisplayActivity> sut = ActivityScenario.launch(FileDisplayActivity.class);
|
||||
FileDisplayActivity sut = activityRule.launchActivity(null);
|
||||
|
||||
// given test folder
|
||||
assertTrue(new CreateFolderOperation("/test/", true).execute(client, getStorageManager()).isSuccess());
|
||||
assertTrue(new CreateFolderOperation("/test/", account, targetContext)
|
||||
.execute(client, getStorageManager())
|
||||
.isSuccess());
|
||||
|
||||
// navigate into it
|
||||
OCFile test = getStorageManager().getFileByPath("/test/");
|
||||
|
|
|
@ -10,27 +10,37 @@ import android.content.Context;
|
|||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.evernote.android.job.JobRequest;
|
||||
import com.facebook.testing.screenshot.Screenshot;
|
||||
import com.nextcloud.client.RetryTestRule;
|
||||
import com.nextcloud.client.account.UserAccountManager;
|
||||
import com.nextcloud.client.account.UserAccountManagerImpl;
|
||||
import com.nextcloud.client.device.PowerManagementService;
|
||||
import com.nextcloud.client.network.ConnectivityService;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||
import com.owncloud.android.lib.common.OwnCloudClientFactory;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.resources.e2ee.ToggleEncryptionRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.ReadFolderRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.model.RemoteFile;
|
||||
import com.owncloud.android.operations.CreateFolderOperation;
|
||||
import com.owncloud.android.operations.UploadFileOperation;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
|
@ -56,7 +66,7 @@ import static org.junit.Assert.assertTrue;
|
|||
|
||||
//@RunWith(AndroidJUnit4.class)
|
||||
public abstract class AbstractIT {
|
||||
@Rule public RetryTestRule retryTestRule = new RetryTestRule();
|
||||
// @Rule public RetryTestRule retryTestRule = new RetryTestRule();
|
||||
|
||||
protected static OwnCloudClient client;
|
||||
protected static Account account;
|
||||
|
@ -99,7 +109,7 @@ public abstract class AbstractIT {
|
|||
|
||||
waitForServer(client, baseUrl);
|
||||
|
||||
deleteAllFiles(); // makes sure that no file/folder is in root
|
||||
// deleteAllFiles(); // makes sure that no file/folder is in root
|
||||
} catch (OperationCanceledException e) {
|
||||
e.printStackTrace();
|
||||
} catch (AuthenticatorException e) {
|
||||
|
@ -124,8 +134,18 @@ public abstract class AbstractIT {
|
|||
RemoteFile remoteFile = (RemoteFile) object;
|
||||
|
||||
if (!remoteFile.getRemotePath().equals("/")) {
|
||||
if (remoteFile.isEncrypted()) {
|
||||
assertTrue(new ToggleEncryptionRemoteOperation(remoteFile.getLocalId(),
|
||||
remoteFile.getRemotePath(),
|
||||
false)
|
||||
.execute(client)
|
||||
.isSuccess());
|
||||
}
|
||||
|
||||
assertTrue(new RemoveFileRemoteOperation(remoteFile.getRemotePath())
|
||||
.execute(client).isSuccess());
|
||||
.execute(client)
|
||||
.isSuccess()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +167,7 @@ public abstract class AbstractIT {
|
|||
createFile("chunkedFile.txt", 500000);
|
||||
}
|
||||
|
||||
private static void createFile(String name, int iteration) throws IOException {
|
||||
public static void createFile(String name, int iteration) throws IOException {
|
||||
File file = new File(FileStorageUtils.getSavePath(account.name) + File.separator + name);
|
||||
file.createNewFile();
|
||||
|
||||
|
@ -235,4 +255,74 @@ public abstract class AbstractIT {
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public OCFile createFolder(String remotePath) {
|
||||
TestCase.assertTrue(new CreateFolderOperation(remotePath, account, targetContext)
|
||||
.execute(client, getStorageManager())
|
||||
.isSuccess());
|
||||
|
||||
return getStorageManager().getFileByDecryptedRemotePath(remotePath);
|
||||
}
|
||||
|
||||
public void uploadOCUpload(OCUpload ocUpload) {
|
||||
ConnectivityService connectivityServiceMock = new ConnectivityService() {
|
||||
@Override
|
||||
public boolean isInternetWalled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnlineWithWifi() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobRequest.NetworkType getActiveNetworkType() {
|
||||
return JobRequest.NetworkType.ANY;
|
||||
}
|
||||
};
|
||||
|
||||
PowerManagementService powerManagementServiceMock = new PowerManagementService() {
|
||||
@Override
|
||||
public boolean isPowerSavingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSavingExclusionAvailable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatteryCharging() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
UserAccountManager accountManager = UserAccountManagerImpl.fromContext(targetContext);
|
||||
UploadsStorageManager uploadsStorageManager = new UploadsStorageManager(accountManager,
|
||||
targetContext.getContentResolver());
|
||||
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
account,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false
|
||||
);
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
});
|
||||
|
||||
newUpload.setRemoteFolderToBeCreated();
|
||||
|
||||
RemoteOperationResult result = newUpload.execute(client, getStorageManager());
|
||||
assertTrue(result.getLogMessage(), result.isSuccess());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.owncloud.android;
|
||||
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.operations.CreateFolderOperation;
|
||||
import com.owncloud.android.operations.RemoveFileOperation;
|
||||
|
@ -26,16 +27,17 @@ public class FileIT extends AbstractIT {
|
|||
// folder does not exist yet
|
||||
assertNull(getStorageManager().getFileByPath(path));
|
||||
|
||||
SyncOperation syncOp = new CreateFolderOperation(path, true);
|
||||
SyncOperation syncOp = new CreateFolderOperation(path, account, targetContext);
|
||||
RemoteOperationResult result = syncOp.execute(client, getStorageManager());
|
||||
|
||||
assertTrue(result.toString(), result.isSuccess());
|
||||
|
||||
// folder exists
|
||||
assertTrue(getStorageManager().getFileByPath(path).isFolder());
|
||||
OCFile file = getStorageManager().getFileByPath(path);
|
||||
assertTrue(file.isFolder());
|
||||
|
||||
// cleanup
|
||||
new RemoveFileOperation(path, false, account, false, targetContext).execute(client, getStorageManager());
|
||||
new RemoveFileOperation(file, false, account, false, targetContext).execute(client, getStorageManager());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -44,14 +46,20 @@ public class FileIT extends AbstractIT {
|
|||
// folder does not exist yet
|
||||
assertNull(getStorageManager().getFileByPath(path));
|
||||
|
||||
SyncOperation syncOp = new CreateFolderOperation(path, true);
|
||||
SyncOperation syncOp = new CreateFolderOperation(path, account, targetContext);
|
||||
RemoteOperationResult result = syncOp.execute(client, getStorageManager());
|
||||
assertTrue(result.toString(), result.isSuccess());
|
||||
|
||||
// folder exists
|
||||
assertTrue(getStorageManager().getFileByPath(path).isFolder());
|
||||
OCFile file = getStorageManager().getFileByPath(path);
|
||||
assertTrue(file.isFolder());
|
||||
|
||||
// cleanup
|
||||
new RemoveFileOperation("/testFolder/", false, account, false, targetContext).execute(client, getStorageManager());
|
||||
new RemoveFileOperation(file,
|
||||
false,
|
||||
account,
|
||||
false,
|
||||
targetContext)
|
||||
.execute(client, getStorageManager());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ public class ScreenshotsIT extends AbstractIT {
|
|||
|
||||
// folder does not exist yet
|
||||
if (getStorageManager().getFileByPath(path) == null) {
|
||||
SyncOperation syncOp = new CreateFolderOperation(path, true);
|
||||
SyncOperation syncOp = new CreateFolderOperation(path, account, targetContext);
|
||||
RemoteOperationResult result = syncOp.execute(client, getStorageManager());
|
||||
|
||||
assertTrue(result.isSuccess());
|
||||
|
|
|
@ -30,11 +30,9 @@ import com.nextcloud.client.device.PowerManagementService;
|
|||
import com.nextcloud.client.network.Connectivity;
|
||||
import com.nextcloud.client.network.ConnectivityService;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.operations.RemoveFileOperation;
|
||||
import com.owncloud.android.operations.UploadFileOperation;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -44,8 +42,6 @@ import org.junit.runner.RunWith;
|
|||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests related to file uploads
|
||||
*/
|
||||
|
@ -95,92 +91,66 @@ public class UploadIT extends AbstractIT {
|
|||
@Test
|
||||
public void testEmptyUpload() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/empty.txt",
|
||||
"/testUpload/empty.txt", account.name);
|
||||
"/testUpload/empty.txt",
|
||||
account.name);
|
||||
|
||||
RemoteOperationResult result = testUpload(ocUpload);
|
||||
|
||||
assertTrue(result.toString(), result.isSuccess());
|
||||
uploadOCUpload(ocUpload);
|
||||
|
||||
// cleanup
|
||||
new RemoveFileOperation("/testUpload/", false, account, false, targetContext).execute(client, getStorageManager());
|
||||
new RemoveFileOperation(new OCFile("/testUpload/"),
|
||||
false,
|
||||
account,
|
||||
false,
|
||||
targetContext)
|
||||
.execute(client, getStorageManager());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonEmptyUpload() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/nonEmpty.txt",
|
||||
"/testUpload/nonEmpty.txt", account.name);
|
||||
"/testUpload/nonEmpty.txt",
|
||||
account.name);
|
||||
|
||||
RemoteOperationResult result = testUpload(ocUpload);
|
||||
|
||||
assertTrue(result.toString(), result.isSuccess());
|
||||
uploadOCUpload(ocUpload);
|
||||
|
||||
// cleanup
|
||||
new RemoveFileOperation("/testUpload/", false, account, false, targetContext).execute(client, getStorageManager());
|
||||
new RemoveFileOperation(new OCFile("/testUpload/"),
|
||||
false,
|
||||
account,
|
||||
false,
|
||||
targetContext)
|
||||
.execute(client, getStorageManager());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChunkedUpload() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/chunkedFile.txt",
|
||||
"/testUpload/chunkedFile.txt", account.name);
|
||||
"/testUpload/chunkedFile.txt", account.name);
|
||||
|
||||
RemoteOperationResult result = testUpload(ocUpload);
|
||||
|
||||
assertTrue(result.toString(), result.isSuccess());
|
||||
uploadOCUpload(ocUpload);
|
||||
|
||||
// cleanup
|
||||
new RemoveFileOperation("/testUpload/", false, account, false, targetContext).execute(client, getStorageManager());
|
||||
}
|
||||
|
||||
public RemoteOperationResult testUpload(OCUpload ocUpload) {
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
storageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
account,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false
|
||||
);
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
});
|
||||
|
||||
newUpload.setRemoteFolderToBeCreated();
|
||||
|
||||
return newUpload.execute(client, getStorageManager());
|
||||
new RemoveFileOperation(new OCFile("/testUpload/"),
|
||||
false,
|
||||
account,
|
||||
false,
|
||||
targetContext)
|
||||
.execute(client, getStorageManager());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadInNonExistingFolder() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/empty.txt",
|
||||
"/testUpload/2/3/4/1.txt", account.name);
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
storageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
account,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false
|
||||
);
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
});
|
||||
"/testUpload/2/3/4/1.txt", account.name);
|
||||
|
||||
newUpload.setRemoteFolderToBeCreated();
|
||||
|
||||
RemoteOperationResult result = newUpload.execute(client, getStorageManager());
|
||||
assertTrue(result.toString(), result.isSuccess());
|
||||
uploadOCUpload(ocUpload);
|
||||
|
||||
// cleanup
|
||||
new RemoveFileOperation("/testUpload/", false, account, false, targetContext).execute(client, getStorageManager());
|
||||
new RemoveFileOperation(new OCFile("/testUpload/"),
|
||||
false,
|
||||
account,
|
||||
false,
|
||||
targetContext)
|
||||
.execute(client, getStorageManager());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public class FolderPickerActivityIT {
|
|||
// Arrange
|
||||
FolderPickerActivity targetActivity = activityRule.getActivity();
|
||||
OCFile origin = new OCFile("/test/file.test");
|
||||
origin.setRemotePath("/remotePath/test");
|
||||
origin.setEncryptedRemotePath("/remotePath/test");
|
||||
|
||||
// Act
|
||||
targetActivity.setFile(origin);
|
||||
|
@ -60,7 +60,7 @@ public class FolderPickerActivityIT {
|
|||
FolderPickerActivity targetActivity = activityRule.getActivity();
|
||||
OCFile origin = new OCFile("/test/");
|
||||
origin.setFileId(1);
|
||||
origin.setRemotePath("/test/");
|
||||
origin.setEncryptedRemotePath("/test/");
|
||||
origin.setStoragePath("/test/");
|
||||
origin.setFolder();
|
||||
|
||||
|
@ -78,7 +78,7 @@ public class FolderPickerActivityIT {
|
|||
FolderPickerActivity targetActivity = activityRule.getActivity();
|
||||
OCFile origin = new OCFile("/");
|
||||
origin.setFileId(1);
|
||||
origin.setRemotePath("/");
|
||||
origin.setEncryptedRemotePath("/");
|
||||
origin.setStoragePath("/");
|
||||
origin.setFolder();
|
||||
|
||||
|
@ -109,7 +109,7 @@ public class FolderPickerActivityIT {
|
|||
// Arrange
|
||||
FolderPickerActivity targetActivity = activityRule.getActivity();
|
||||
OCFile origin = new OCFile("/test/file.test");
|
||||
origin.setRemotePath("/test/file.test");
|
||||
origin.setEncryptedRemotePath("/test/file.test");
|
||||
|
||||
OCFile target = new OCFile("/test/");
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ class OCFileListFragmentIT : AbstractIT() {
|
|||
|
||||
@Test
|
||||
fun showRichWorkspace() {
|
||||
assertTrue(CreateFolderOperation("/test/", true).execute(client, storageManager).isSuccess)
|
||||
assertTrue(CreateFolderOperation("/test/", account, targetContext).execute(client, storageManager).isSuccess)
|
||||
|
||||
val ocUpload = OCUpload(FileStorageUtils.getSavePath(account.name) + "/nonEmpty.txt",
|
||||
"/test/Readme.md",
|
||||
|
@ -159,7 +159,9 @@ class OCFileListFragmentIT : AbstractIT() {
|
|||
@Test
|
||||
fun createAndShowShareToUser() {
|
||||
val path = "/shareToAdmin/"
|
||||
TestCase.assertTrue(CreateFolderOperation(path, true).execute(client, storageManager).isSuccess)
|
||||
TestCase.assertTrue(CreateFolderOperation(path, account, targetContext)
|
||||
.execute(client, storageManager)
|
||||
.isSuccess)
|
||||
|
||||
// share folder to user "admin"
|
||||
TestCase.assertTrue(CreateShareRemoteOperation(path,
|
||||
|
@ -181,7 +183,9 @@ class OCFileListFragmentIT : AbstractIT() {
|
|||
@Test
|
||||
fun createAndShowShareToGroup() {
|
||||
val path = "/shareToGroup/"
|
||||
TestCase.assertTrue(CreateFolderOperation(path, true).execute(client, storageManager).isSuccess)
|
||||
TestCase.assertTrue(CreateFolderOperation(path, account, targetContext)
|
||||
.execute(client, storageManager)
|
||||
.isSuccess)
|
||||
|
||||
// share folder to group
|
||||
assertTrue(CreateShareRemoteOperation("/shareToGroup/",
|
||||
|
@ -203,7 +207,9 @@ class OCFileListFragmentIT : AbstractIT() {
|
|||
@Test
|
||||
fun createAndShowShareToCircle() {
|
||||
val path = "/shareToCircle/"
|
||||
TestCase.assertTrue(CreateFolderOperation(path, true).execute(client, storageManager).isSuccess)
|
||||
TestCase.assertTrue(CreateFolderOperation(path, account, targetContext)
|
||||
.execute(client, storageManager)
|
||||
.isSuccess)
|
||||
|
||||
// share folder to circle
|
||||
// get circleId
|
||||
|
@ -232,7 +238,9 @@ class OCFileListFragmentIT : AbstractIT() {
|
|||
@Test
|
||||
fun createAndShowShareViaLink() {
|
||||
val path = "/shareViaLink/"
|
||||
TestCase.assertTrue(CreateFolderOperation(path, true).execute(client, storageManager).isSuccess)
|
||||
TestCase.assertTrue(CreateFolderOperation(path, account, targetContext)
|
||||
.execute(client, storageManager)
|
||||
.isSuccess)
|
||||
|
||||
// share folder via public link
|
||||
TestCase.assertTrue(CreateShareRemoteOperation("/shareViaLink/",
|
||||
|
|
|
@ -22,15 +22,19 @@
|
|||
package com.owncloud.android.util;
|
||||
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.owncloud.android.datamodel.DecryptedFolderMetadata;
|
||||
import com.owncloud.android.datamodel.EncryptedFolderMetadata;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.utils.CsrHelper;
|
||||
import com.owncloud.android.utils.EncryptionUtils;
|
||||
|
||||
import net.bytebuddy.utility.RandomString;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -52,6 +56,26 @@ import androidx.annotation.RequiresApi;
|
|||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.EncryptedFile;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decodeStringToBase64Bytes;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decryptFile;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decryptFolderMetaData;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decryptPrivateKey;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decryptStringAsymmetric;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decryptStringSymmetric;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.deserializeJSON;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.encodeBytesToBase64String;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.encryptFile;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.encryptFolderMetadata;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.generateKey;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.generateSHA512;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.getMD5Sum;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.ivDelimiter;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.ivLength;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.randomBytes;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.saltLength;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.serializeJSON;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.verifySHA512;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -110,25 +134,60 @@ public class EncryptionTestIT {
|
|||
|
||||
@Test
|
||||
public void encryptStringAsymmetric() throws Exception {
|
||||
byte[] key1 = EncryptionUtils.generateKey();
|
||||
String base64encodedKey = EncryptionUtils.encodeBytesToBase64String(key1);
|
||||
byte[] key1 = generateKey();
|
||||
String base64encodedKey = encodeBytesToBase64String(key1);
|
||||
|
||||
String encryptedString = EncryptionUtils.encryptStringAsymmetric(base64encodedKey, cert);
|
||||
String decryptedString = EncryptionUtils.decryptStringAsymmetric(encryptedString, privateKey);
|
||||
String decryptedString = decryptStringAsymmetric(encryptedString, privateKey);
|
||||
|
||||
byte[] key2 = EncryptionUtils.decodeStringToBase64Bytes(decryptedString);
|
||||
byte[] key2 = decodeStringToBase64Bytes(decryptedString);
|
||||
|
||||
assertTrue(Arrays.equals(key1, key2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encryptStringSymmetricRandom() throws Exception {
|
||||
int max = 500;
|
||||
for (int i = 0; i < max; i++) {
|
||||
Log_OC.d("EncryptionTestIT", i + " of " + max);
|
||||
byte[] key = generateKey();
|
||||
|
||||
String encryptedString = EncryptionUtils.encryptStringSymmetric(privateKey, key);
|
||||
String decryptedString = decryptStringSymmetric(encryptedString, key);
|
||||
|
||||
assertEquals(privateKey, decryptedString);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encryptStringSymmetric() throws Exception {
|
||||
byte[] key = EncryptionUtils.generateKey();
|
||||
int max = 5000;
|
||||
byte[] key = generateKey();
|
||||
|
||||
String encryptedString = EncryptionUtils.encryptStringSymmetric(privateKey, key);
|
||||
String decryptedString = EncryptionUtils.decryptStringSymmetric(encryptedString, key);
|
||||
for (int i = 0; i < max; i++) {
|
||||
Log_OC.d("EncryptionTestIT", i + " of " + max);
|
||||
|
||||
assertEquals(privateKey, decryptedString);
|
||||
String encryptedString = EncryptionUtils.encryptStringSymmetric(privateKey, key);
|
||||
|
||||
int delimiterPosition = encryptedString.indexOf(ivDelimiter);
|
||||
if (delimiterPosition == -1) {
|
||||
throw new RuntimeException("IV not found!");
|
||||
}
|
||||
|
||||
String ivString = encryptedString.substring(delimiterPosition + ivDelimiter.length());
|
||||
if (TextUtils.isEmpty(ivString)) {
|
||||
delimiterPosition = encryptedString.lastIndexOf(ivDelimiter);
|
||||
ivString = encryptedString.substring(delimiterPosition + ivDelimiter.length());
|
||||
|
||||
if (TextUtils.isEmpty(ivString)) {
|
||||
throw new RuntimeException("IV string is empty");
|
||||
}
|
||||
}
|
||||
|
||||
String decryptedString = decryptStringSymmetric(encryptedString, key);
|
||||
|
||||
assertEquals(privateKey, decryptedString);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -140,10 +199,10 @@ public class EncryptionTestIT {
|
|||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
byte[] privateKeyBytes = privateKey.getEncoded();
|
||||
String privateKeyString = EncryptionUtils.encodeBytesToBase64String(privateKeyBytes);
|
||||
String privateKeyString = encodeBytesToBase64String(privateKeyBytes);
|
||||
|
||||
String encryptedString = EncryptionUtils.encryptPrivateKey(privateKeyString, keyPhrase);
|
||||
String decryptedString = EncryptionUtils.decryptPrivateKey(encryptedString, keyPhrase);
|
||||
String decryptedString = decryptPrivateKey(encryptedString, keyPhrase);
|
||||
|
||||
assertEquals(privateKeyString, decryptedString);
|
||||
}
|
||||
|
@ -155,7 +214,7 @@ public class EncryptionTestIT {
|
|||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
|
||||
assertFalse(CsrHelper.generateCsrPemEncodedString(keyPair, "").isEmpty());
|
||||
assertFalse(EncryptionUtils.encodeBytesToBase64String(keyPair.getPublic().getEncoded()).isEmpty());
|
||||
assertFalse(encodeBytesToBase64String(keyPair.getPublic().getEncoded()).isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -167,31 +226,31 @@ public class EncryptionTestIT {
|
|||
DecryptedFolderMetadata decryptedFolderMetadata1 = generateFolderMetadata();
|
||||
|
||||
// encrypt
|
||||
EncryptedFolderMetadata encryptedFolderMetadata1 = EncryptionUtils.encryptFolderMetadata(
|
||||
EncryptedFolderMetadata encryptedFolderMetadata1 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1, privateKey);
|
||||
|
||||
// serialize
|
||||
String encryptedJson = EncryptionUtils.serializeJSON(encryptedFolderMetadata1);
|
||||
String encryptedJson = serializeJSON(encryptedFolderMetadata1);
|
||||
|
||||
// de-serialize
|
||||
EncryptedFolderMetadata encryptedFolderMetadata2 = EncryptionUtils.deserializeJSON(encryptedJson,
|
||||
new TypeToken<EncryptedFolderMetadata>() {
|
||||
EncryptedFolderMetadata encryptedFolderMetadata2 = deserializeJSON(encryptedJson,
|
||||
new TypeToken<EncryptedFolderMetadata>() {
|
||||
});
|
||||
|
||||
// decrypt
|
||||
DecryptedFolderMetadata decryptedFolderMetadata2 = EncryptionUtils.decryptFolderMetaData(
|
||||
DecryptedFolderMetadata decryptedFolderMetadata2 = decryptFolderMetaData(
|
||||
encryptedFolderMetadata2, privateKey);
|
||||
|
||||
// compare
|
||||
assertTrue(compareJsonStrings(EncryptionUtils.serializeJSON(decryptedFolderMetadata1),
|
||||
EncryptionUtils.serializeJSON(decryptedFolderMetadata2)));
|
||||
assertTrue(compareJsonStrings(serializeJSON(decryptedFolderMetadata1),
|
||||
serializeJSON(decryptedFolderMetadata2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCryptFileWithoutMetadata() throws Exception {
|
||||
byte[] key = EncryptionUtils.decodeStringToBase64Bytes("WANM0gRv+DhaexIsI0T3Lg==");
|
||||
byte[] iv = EncryptionUtils.decodeStringToBase64Bytes("gKm3n+mJzeY26q4OfuZEqg==");
|
||||
byte[] authTag = EncryptionUtils.decodeStringToBase64Bytes("PboI9tqHHX3QeAA22PIu4w==");
|
||||
byte[] key = decodeStringToBase64Bytes("WANM0gRv+DhaexIsI0T3Lg==");
|
||||
byte[] iv = decodeStringToBase64Bytes("gKm3n+mJzeY26q4OfuZEqg==");
|
||||
byte[] authTag = decodeStringToBase64Bytes("PboI9tqHHX3QeAA22PIu4w==");
|
||||
|
||||
assertTrue(cryptFile("ia7OEEEyXMoRa1QWQk8r", "78f42172166f9dc8fd1a7156b1753353", key, iv, authTag));
|
||||
}
|
||||
|
@ -202,25 +261,104 @@ public class EncryptionTestIT {
|
|||
|
||||
// n9WXAIXO2wRY4R8nXwmo
|
||||
assertTrue(cryptFile("ia7OEEEyXMoRa1QWQk8r",
|
||||
"78f42172166f9dc8fd1a7156b1753353",
|
||||
EncryptionUtils.decodeStringToBase64Bytes(metadata.getFiles().get("ia7OEEEyXMoRa1QWQk8r")
|
||||
"78f42172166f9dc8fd1a7156b1753353",
|
||||
decodeStringToBase64Bytes(metadata.getFiles().get("ia7OEEEyXMoRa1QWQk8r")
|
||||
.getEncrypted().getKey()),
|
||||
EncryptionUtils.decodeStringToBase64Bytes(metadata.getFiles().get("ia7OEEEyXMoRa1QWQk8r")
|
||||
decodeStringToBase64Bytes(metadata.getFiles().get("ia7OEEEyXMoRa1QWQk8r")
|
||||
.getInitializationVector()),
|
||||
EncryptionUtils.decodeStringToBase64Bytes(metadata.getFiles().get("ia7OEEEyXMoRa1QWQk8r")
|
||||
decodeStringToBase64Bytes(metadata.getFiles().get("ia7OEEEyXMoRa1QWQk8r")
|
||||
.getAuthenticationTag())));
|
||||
|
||||
// n9WXAIXO2wRY4R8nXwmo
|
||||
assertTrue(cryptFile("n9WXAIXO2wRY4R8nXwmo",
|
||||
"825143ed1f21ebb0c3b3c3f005b2f5db",
|
||||
EncryptionUtils.decodeStringToBase64Bytes(metadata.getFiles().get("n9WXAIXO2wRY4R8nXwmo")
|
||||
"825143ed1f21ebb0c3b3c3f005b2f5db",
|
||||
decodeStringToBase64Bytes(metadata.getFiles().get("n9WXAIXO2wRY4R8nXwmo")
|
||||
.getEncrypted().getKey()),
|
||||
EncryptionUtils.decodeStringToBase64Bytes(metadata.getFiles().get("n9WXAIXO2wRY4R8nXwmo")
|
||||
decodeStringToBase64Bytes(metadata.getFiles().get("n9WXAIXO2wRY4R8nXwmo")
|
||||
.getInitializationVector()),
|
||||
EncryptionUtils.decodeStringToBase64Bytes(metadata.getFiles().get("n9WXAIXO2wRY4R8nXwmo")
|
||||
decodeStringToBase64Bytes(metadata.getFiles().get("n9WXAIXO2wRY4R8nXwmo")
|
||||
.getAuthenticationTag())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bigMetadata() throws Exception {
|
||||
DecryptedFolderMetadata decryptedFolderMetadata1 = generateFolderMetadata();
|
||||
|
||||
// encrypt
|
||||
EncryptedFolderMetadata encryptedFolderMetadata1 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1, privateKey);
|
||||
|
||||
// serialize
|
||||
String encryptedJson = serializeJSON(encryptedFolderMetadata1);
|
||||
|
||||
// de-serialize
|
||||
EncryptedFolderMetadata encryptedFolderMetadata2 = deserializeJSON(encryptedJson,
|
||||
new TypeToken<EncryptedFolderMetadata>() {
|
||||
});
|
||||
|
||||
// decrypt
|
||||
DecryptedFolderMetadata decryptedFolderMetadata2 = decryptFolderMetaData(
|
||||
encryptedFolderMetadata2, privateKey);
|
||||
|
||||
// compare
|
||||
assertTrue(compareJsonStrings(serializeJSON(decryptedFolderMetadata1),
|
||||
serializeJSON(decryptedFolderMetadata2)));
|
||||
|
||||
// prefill with 500
|
||||
for (int i = 0; i < 500; i++) {
|
||||
addFile(decryptedFolderMetadata1, i);
|
||||
}
|
||||
|
||||
int max = 505;
|
||||
for (int i = 500; i < max; i++) {
|
||||
Log_OC.d(this, "Big metadata: " + i + " of " + max);
|
||||
|
||||
addFile(decryptedFolderMetadata1, i);
|
||||
|
||||
// encrypt
|
||||
encryptedFolderMetadata1 = encryptFolderMetadata(decryptedFolderMetadata1, privateKey);
|
||||
|
||||
// serialize
|
||||
encryptedJson = serializeJSON(encryptedFolderMetadata1);
|
||||
|
||||
// de-serialize
|
||||
encryptedFolderMetadata2 = deserializeJSON(encryptedJson,
|
||||
new TypeToken<EncryptedFolderMetadata>() {
|
||||
});
|
||||
|
||||
// decrypt
|
||||
decryptedFolderMetadata2 = decryptFolderMetaData(encryptedFolderMetadata2, privateKey);
|
||||
|
||||
// compare
|
||||
assertTrue(compareJsonStrings(serializeJSON(decryptedFolderMetadata1),
|
||||
serializeJSON(decryptedFolderMetadata2)));
|
||||
|
||||
assertEquals(i + 3, decryptedFolderMetadata1.getFiles().size());
|
||||
assertEquals(i + 3, decryptedFolderMetadata2.getFiles().size());
|
||||
}
|
||||
}
|
||||
|
||||
private void addFile(DecryptedFolderMetadata decryptedFolderMetadata, int counter) {
|
||||
// Add new file
|
||||
// Always generate new
|
||||
byte[] key = generateKey();
|
||||
byte[] iv = randomBytes(ivLength);
|
||||
byte[] authTag = randomBytes((128 / 8));
|
||||
|
||||
DecryptedFolderMetadata.Data data = new DecryptedFolderMetadata.Data();
|
||||
data.setKey(EncryptionUtils.encodeBytesToBase64String(key));
|
||||
data.setFilename(counter + ".txt");
|
||||
data.setVersion(1);
|
||||
|
||||
DecryptedFolderMetadata.DecryptedFile file = new DecryptedFolderMetadata.DecryptedFile();
|
||||
file.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(iv));
|
||||
file.setEncrypted(data);
|
||||
file.setMetadataKey(0);
|
||||
file.setAuthenticationTag(EncryptionUtils.encodeBytesToBase64String(authTag));
|
||||
|
||||
decryptedFolderMetadata.getFiles().put(RandomString.make(20), file);
|
||||
}
|
||||
|
||||
/**
|
||||
* generates new keys and tests if they are unique
|
||||
*/
|
||||
|
@ -229,7 +367,7 @@ public class EncryptionTestIT {
|
|||
Set<String> keys = new HashSet<>();
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
assertTrue(keys.add(EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey())));
|
||||
assertTrue(keys.add(encodeBytesToBase64String(generateKey())));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,8 +379,8 @@ public class EncryptionTestIT {
|
|||
Set<String> ivs = new HashSet<>();
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
assertTrue(ivs.add(EncryptionUtils.encodeBytesToBase64String(
|
||||
EncryptionUtils.randomBytes(EncryptionUtils.ivLength))));
|
||||
assertTrue(ivs.add(encodeBytesToBase64String(
|
||||
randomBytes(ivLength))));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,8 +392,8 @@ public class EncryptionTestIT {
|
|||
Set<String> ivs = new HashSet<>();
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
assertTrue(ivs.add(EncryptionUtils.encodeBytesToBase64String(
|
||||
EncryptionUtils.randomBytes(EncryptionUtils.saltLength))));
|
||||
assertTrue(ivs.add(encodeBytesToBase64String(
|
||||
randomBytes(saltLength))));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,13 +401,13 @@ public class EncryptionTestIT {
|
|||
public void testSHA512() {
|
||||
// sent to 3rd party app in cleartext
|
||||
String token = "4ae5978bf5354cd284b539015d442141";
|
||||
String salt = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.randomBytes(EncryptionUtils.saltLength));
|
||||
String salt = encodeBytesToBase64String(randomBytes(saltLength));
|
||||
|
||||
// stored in database
|
||||
String hashedToken = EncryptionUtils.generateSHA512(token, salt);
|
||||
String hashedToken = generateSHA512(token, salt);
|
||||
|
||||
// check: use passed cleartext and salt to verify hashed token
|
||||
assertTrue(EncryptionUtils.verifySHA512(hashedToken, token));
|
||||
assertTrue(verifySHA512(hashedToken, token));
|
||||
}
|
||||
|
||||
|
||||
|
@ -289,9 +427,9 @@ public class EncryptionTestIT {
|
|||
}
|
||||
|
||||
private DecryptedFolderMetadata generateFolderMetadata() throws Exception {
|
||||
String metadataKey0 = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey());
|
||||
String metadataKey1 = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey());
|
||||
String metadataKey2 = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey());
|
||||
String metadataKey0 = encodeBytesToBase64String(generateKey());
|
||||
String metadataKey1 = encodeBytesToBase64String(generateKey());
|
||||
String metadataKey2 = encodeBytesToBase64String(generateKey());
|
||||
HashMap<Integer, String> metadataKeys = new HashMap<>();
|
||||
metadataKeys.put(0, EncryptionUtils.encryptStringAsymmetric(metadataKey0, cert));
|
||||
metadataKeys.put(1, EncryptionUtils.encryptStringAsymmetric(metadataKey1, cert));
|
||||
|
@ -345,28 +483,28 @@ public class EncryptionTestIT {
|
|||
private boolean cryptFile(String fileName, String md5, byte[] key, byte[] iv, byte[] expectedAuthTag)
|
||||
throws Exception {
|
||||
File file = getFile(fileName);
|
||||
assertEquals(md5, EncryptionUtils.getMD5Sum(file));
|
||||
assertEquals(md5, getMD5Sum(file));
|
||||
|
||||
EncryptionUtils.EncryptedFile encryptedFile = EncryptionUtils.encryptFile(file, key, iv);
|
||||
EncryptedFile encryptedFile = encryptFile(file, key, iv);
|
||||
|
||||
File encryptedTempFile = File.createTempFile("file", "tmp");
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(encryptedTempFile);
|
||||
fileOutputStream.write(encryptedFile.encryptedBytes);
|
||||
fileOutputStream.close();
|
||||
|
||||
byte[] authenticationTag = EncryptionUtils.decodeStringToBase64Bytes(encryptedFile.authenticationTag);
|
||||
byte[] authenticationTag = decodeStringToBase64Bytes(encryptedFile.authenticationTag);
|
||||
|
||||
// verify authentication tag
|
||||
assertTrue(Arrays.equals(expectedAuthTag, authenticationTag));
|
||||
|
||||
byte[] decryptedBytes = EncryptionUtils.decryptFile(encryptedTempFile, key, iv, authenticationTag);
|
||||
byte[] decryptedBytes = decryptFile(encryptedTempFile, key, iv, authenticationTag);
|
||||
|
||||
File decryptedFile = File.createTempFile("file", "dec");
|
||||
FileOutputStream fileOutputStream1 = new FileOutputStream(decryptedFile);
|
||||
fileOutputStream1.write(decryptedBytes);
|
||||
fileOutputStream1.close();
|
||||
|
||||
return md5.compareTo(EncryptionUtils.getMD5Sum(decryptedFile)) == 0;
|
||||
return md5.compareTo(getMD5Sum(decryptedFile)) == 0;
|
||||
}
|
||||
|
||||
private File getFile(String filename) throws IOException {
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.accounts.Account;
|
|||
import android.content.res.Resources;
|
||||
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.operations.RemoveFileOperation;
|
||||
import com.owncloud.android.utils.ErrorMessageAdapter;
|
||||
|
@ -50,7 +51,7 @@ public class ErrorMessageAdapterIT {
|
|||
|
||||
String errorMessage = ErrorMessageAdapter.getErrorCauseMessage(
|
||||
new RemoteOperationResult(RemoteOperationResult.ResultCode.FORBIDDEN),
|
||||
new RemoveFileOperation(PATH_TO_DELETE, false, account, false, MainApp.getAppContext()),
|
||||
new RemoveFileOperation(new OCFile(PATH_TO_DELETE), false, account, false, MainApp.getAppContext()),
|
||||
resources
|
||||
);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.annotation.Nullable;
|
|||
public interface CurrentAccountProvider {
|
||||
/**
|
||||
* Get currently active account.
|
||||
* Replaced by getUser()
|
||||
*
|
||||
* @return Currently selected {@link Account} or first valid {@link Account} registered in OS or null, if not available at all.
|
||||
*/
|
||||
|
|
|
@ -93,7 +93,7 @@ class OfflineSyncWork constructor(
|
|||
val ocFolder = storageManager.getFileByPath(folderName)
|
||||
Log_OC.d(TAG, folderName + ": currentEtag: " + ocFolder.etag)
|
||||
// check for etag change, if false, skip
|
||||
val checkEtagOperation = CheckEtagRemoteOperation(ocFolder.remotePath,
|
||||
val checkEtagOperation = CheckEtagRemoteOperation(ocFolder.encryptedFileName,
|
||||
ocFolder.etagOnServer)
|
||||
val result = checkEtagOperation.execute(user.toPlatformAccount(), context)
|
||||
when (result.code) {
|
||||
|
|
|
@ -96,8 +96,25 @@ public class FileDataStorageManager {
|
|||
this.account = account;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use getFileByEncryptedRemotePath() or getFileByDecryptedRemotePath()
|
||||
*/
|
||||
@Deprecated
|
||||
public OCFile getFileByPath(String path) {
|
||||
Cursor c = getFileCursorForValue(ProviderTableMeta.FILE_PATH, path);
|
||||
return getFileByEncryptedRemotePath(path);
|
||||
}
|
||||
|
||||
public OCFile getFileByEncryptedRemotePath(String path) {
|
||||
return getFileByPath(ProviderTableMeta.FILE_PATH, path);
|
||||
}
|
||||
|
||||
public OCFile getFileByDecryptedRemotePath(String path) {
|
||||
return getFileByPath(ProviderTableMeta.FILE_PATH_DECRYPTED, path);
|
||||
}
|
||||
|
||||
private OCFile getFileByPath(String type, String path) {
|
||||
Cursor c = getFileCursorForValue(type, path);
|
||||
OCFile file = null;
|
||||
if (c.moveToFirst()) {
|
||||
file = createFileInstance(c);
|
||||
|
@ -190,8 +207,9 @@ public class FileDataStorageManager {
|
|||
cv.put(ProviderTableMeta.FILE_ENCRYPTED_NAME, file.getEncryptedFileName());
|
||||
cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
|
||||
cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
|
||||
cv.put(ProviderTableMeta.FILE_PATH_DECRYPTED, file.getDecryptedRemotePath());
|
||||
cv.put(ProviderTableMeta.FILE_IS_ENCRYPTED, file.isEncrypted());
|
||||
if (!file.isFolder()) {
|
||||
cv.put(ProviderTableMeta.FILE_IS_ENCRYPTED, file.isEncrypted());
|
||||
cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
|
||||
}
|
||||
cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, account.name);
|
||||
|
@ -215,10 +233,7 @@ public class FileDataStorageManager {
|
|||
cv.put(ProviderTableMeta.FILE_RICH_WORKSPACE, file.getRichWorkspace());
|
||||
|
||||
boolean sameRemotePath = fileExists(file.getRemotePath());
|
||||
if (sameRemotePath ||
|
||||
fileExists(file.getFileId())) { // for renamed files; no more delete and create
|
||||
|
||||
|
||||
if (sameRemotePath || fileExists(file.getFileId())) { // for renamed files; no more delete and create
|
||||
if (sameRemotePath) {
|
||||
OCFile oldFile = getFileByPath(file.getRemotePath());
|
||||
file.setFileId(oldFile.getFileId());
|
||||
|
@ -446,6 +461,7 @@ public class FileDataStorageManager {
|
|||
cv.put(ProviderTableMeta.FILE_NAME, folder.getFileName());
|
||||
cv.put(ProviderTableMeta.FILE_PARENT, folder.getParentId());
|
||||
cv.put(ProviderTableMeta.FILE_PATH, folder.getRemotePath());
|
||||
cv.put(ProviderTableMeta.FILE_PATH_DECRYPTED, folder.getDecryptedRemotePath());
|
||||
cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, account.name);
|
||||
cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, folder.getLastSyncDateForProperties());
|
||||
cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, folder.getLastSyncDateForData());
|
||||
|
@ -479,9 +495,8 @@ public class FileDataStorageManager {
|
|||
cv.put(ProviderTableMeta.FILE_ENCRYPTED_NAME, file.getEncryptedFileName());
|
||||
cv.put(ProviderTableMeta.FILE_PARENT, folder.getFileId());
|
||||
cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
|
||||
if (!file.isFolder()) {
|
||||
cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
|
||||
}
|
||||
cv.put(ProviderTableMeta.FILE_PATH_DECRYPTED, file.getDecryptedRemotePath());
|
||||
cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
|
||||
cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, account.name);
|
||||
cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
|
||||
cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
|
||||
|
@ -659,11 +674,11 @@ public class FileDataStorageManager {
|
|||
if (getContentProviderClient() != null) {
|
||||
try {
|
||||
c = getContentProviderClient().query(
|
||||
ProviderTableMeta.CONTENT_URI,
|
||||
null,
|
||||
ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + " LIKE ? ",
|
||||
new String[]{account.name, file.getRemotePath() + "%"},
|
||||
ProviderTableMeta.FILE_PATH + " ASC "
|
||||
ProviderTableMeta.CONTENT_URI,
|
||||
null,
|
||||
ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + " LIKE ? ",
|
||||
new String[]{account.name, file.getRemotePath() + "%"},
|
||||
ProviderTableMeta.FILE_PATH + " ASC "
|
||||
);
|
||||
} catch (RemoteException e) {
|
||||
Log_OC.e(TAG, e.getMessage(), e);
|
||||
|
@ -671,11 +686,11 @@ public class FileDataStorageManager {
|
|||
|
||||
} else {
|
||||
c = getContentResolver().query(
|
||||
ProviderTableMeta.CONTENT_URI,
|
||||
null,
|
||||
ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + " LIKE ? ",
|
||||
new String[]{account.name, file.getRemotePath() + "%"},
|
||||
ProviderTableMeta.FILE_PATH + " ASC "
|
||||
ProviderTableMeta.CONTENT_URI,
|
||||
null,
|
||||
ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + " LIKE ? ",
|
||||
new String[]{account.name, file.getRemotePath() + "%"},
|
||||
ProviderTableMeta.FILE_PATH + " ASC "
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -692,8 +707,8 @@ public class FileDataStorageManager {
|
|||
ContentValues cv = new ContentValues(); // keep construction in the loop
|
||||
OCFile child = createFileInstance(c);
|
||||
cv.put(
|
||||
ProviderTableMeta.FILE_PATH,
|
||||
targetPath + child.getRemotePath().substring(lengthOfOldPath)
|
||||
ProviderTableMeta.FILE_PATH,
|
||||
targetPath + child.getRemotePath().substring(lengthOfOldPath)
|
||||
);
|
||||
if (child.getStoragePath() != null && child.getStoragePath().startsWith(defaultSavePath)) {
|
||||
// update link to downloaded content - but local move is not done here!
|
||||
|
@ -961,9 +976,9 @@ public class FileDataStorageManager {
|
|||
OCFile file = null;
|
||||
if (c != null) {
|
||||
file = new OCFile(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH)));
|
||||
file.setDecryptedRemotePath(getString(c, ProviderTableMeta.FILE_PATH_DECRYPTED));
|
||||
file.setFileId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
|
||||
file.setParentId(c.getLong(c.getColumnIndex(ProviderTableMeta.FILE_PARENT)));
|
||||
file.setEncryptedFileName(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ENCRYPTED_NAME)));
|
||||
file.setMimeType(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)));
|
||||
file.setStoragePath(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
|
||||
if (file.getStoragePath() == null) {
|
||||
|
@ -995,9 +1010,9 @@ public class FileDataStorageManager {
|
|||
file.setEtagInConflict(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG_IN_CONFLICT)));
|
||||
file.setFavorite(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_FAVORITE)) == 1);
|
||||
file.setEncrypted(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_IS_ENCRYPTED)) == 1);
|
||||
if (file.isEncrypted()) {
|
||||
file.setFileName(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_NAME)));
|
||||
}
|
||||
// if (file.isEncrypted()) {
|
||||
// file.setFileName(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_NAME)));
|
||||
// }
|
||||
file.setMountType(WebdavEntry.MountType.values()[c.getInt(
|
||||
c.getColumnIndex(ProviderTableMeta.FILE_MOUNT_TYPE))]);
|
||||
file.setPreviewAvailable(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_HAS_PREVIEW)) == 1);
|
||||
|
|
|
@ -64,6 +64,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
*/
|
||||
private long modificationTimestampAtLastSyncForData;
|
||||
private String remotePath;
|
||||
private String decryptedRemotePath;
|
||||
private String localPath;
|
||||
private String mimeType;
|
||||
private boolean needsUpdatingWhileSaving;
|
||||
|
@ -103,8 +104,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
* Cached after first call, until changed.
|
||||
*/
|
||||
private Uri exposedFileUri;
|
||||
private String encryptedFileName;
|
||||
|
||||
|
||||
/**
|
||||
* Create new {@link OCFile} with given path.
|
||||
|
@ -135,6 +134,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
modificationTimestamp = source.readLong();
|
||||
modificationTimestampAtLastSyncForData = source.readLong();
|
||||
remotePath = source.readString();
|
||||
decryptedRemotePath = source.readString();
|
||||
localPath = source.readString();
|
||||
mimeType = source.readString();
|
||||
needsUpdatingWhileSaving = source.readInt() == 0;
|
||||
|
@ -152,7 +152,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
sharedWithSharee = source.readInt() == 1;
|
||||
favorite = source.readInt() == 1;
|
||||
encrypted = source.readInt() == 1;
|
||||
encryptedFileName = source.readString();
|
||||
ownerId = source.readString();
|
||||
ownerDisplayName = source.readString();
|
||||
mountType = (WebdavEntry.MountType) source.readSerializable();
|
||||
|
@ -169,6 +168,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
dest.writeLong(modificationTimestamp);
|
||||
dest.writeLong(modificationTimestampAtLastSyncForData);
|
||||
dest.writeString(remotePath);
|
||||
dest.writeString(decryptedRemotePath);
|
||||
dest.writeString(localPath);
|
||||
dest.writeString(mimeType);
|
||||
dest.writeInt(needsUpdatingWhileSaving ? 1 : 0);
|
||||
|
@ -186,7 +186,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
dest.writeInt(sharedWithSharee ? 1 : 0);
|
||||
dest.writeInt(favorite ? 1 : 0);
|
||||
dest.writeInt(encrypted ? 1 : 0);
|
||||
dest.writeString(encryptedFileName);
|
||||
dest.writeString(ownerId);
|
||||
dest.writeString(ownerDisplayName);
|
||||
dest.writeSerializable(mountType);
|
||||
|
@ -194,34 +193,50 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
dest.writeInt(previewAvailable ? 1 : 0);
|
||||
}
|
||||
|
||||
public String getDecryptedRemotePath() {
|
||||
return remotePath;
|
||||
public void setDecryptedRemotePath(String path) {
|
||||
decryptedRemotePath = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remote path of the file on ownCloud
|
||||
* Use decrypted remote path for every local file operation Use encrypted remote path for every dav related
|
||||
* operation
|
||||
*/
|
||||
public String getDecryptedRemotePath() {
|
||||
// Fallback
|
||||
// TODO test without, on a new created folder
|
||||
if (!isEncrypted() && decryptedRemotePath == null) {
|
||||
decryptedRemotePath = remotePath;
|
||||
}
|
||||
|
||||
if (isFolder()) {
|
||||
if (decryptedRemotePath.endsWith(PATH_SEPARATOR)) {
|
||||
return decryptedRemotePath;
|
||||
} else {
|
||||
return decryptedRemotePath + PATH_SEPARATOR;
|
||||
}
|
||||
} else {
|
||||
return decryptedRemotePath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remote path of the file on Nextcloud
|
||||
* (this might be an encrypted file path, if E2E is used)
|
||||
* <p>
|
||||
* Use decrypted remote path for every local file operation.
|
||||
* Use remote path for every dav related operation
|
||||
*
|
||||
* @return The remote path to the file
|
||||
*/
|
||||
public String getRemotePath() {
|
||||
if (isEncrypted() && !isFolder()) {
|
||||
String parentPath = new File(remotePath).getParent();
|
||||
|
||||
if (parentPath.endsWith(PATH_SEPARATOR)) {
|
||||
return parentPath + getEncryptedFileName();
|
||||
if (isFolder()) {
|
||||
if (remotePath.endsWith(PATH_SEPARATOR)) {
|
||||
return remotePath;
|
||||
} else {
|
||||
return parentPath + PATH_SEPARATOR + getEncryptedFileName();
|
||||
return remotePath + PATH_SEPARATOR;
|
||||
}
|
||||
} else {
|
||||
if (isFolder()) {
|
||||
if (remotePath.endsWith(PATH_SEPARATOR)) {
|
||||
return remotePath;
|
||||
} else {
|
||||
return remotePath + PATH_SEPARATOR;
|
||||
}
|
||||
} else {
|
||||
return remotePath;
|
||||
}
|
||||
return remotePath;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,7 +256,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
* @return true if it is a folder
|
||||
*/
|
||||
public boolean isFolder() {
|
||||
return MimeType.DIRECTORY.equals(mimeType);
|
||||
return MimeType.DIRECTORY.equals(mimeType) || MimeType.WEBDAV_FOLDER.equals(mimeType);
|
||||
}
|
||||
|
||||
|
||||
|
@ -346,17 +361,40 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
* @param storage_path to set
|
||||
*/
|
||||
public void setStoragePath(String storage_path) {
|
||||
localPath = storage_path;
|
||||
if (storage_path == null) {
|
||||
localPath = null;
|
||||
} else {
|
||||
localPath = storage_path.replaceAll("//", "/");
|
||||
}
|
||||
localUri = null;
|
||||
exposedFileUri = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filename and "/" for the root directory
|
||||
* Returns the decrypted filename and "/" for the root directory
|
||||
*
|
||||
* @return The name of the file
|
||||
*/
|
||||
public String getFileName() {
|
||||
return getDecryptedFileName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the decrypted filename and "/" for the root directory
|
||||
*
|
||||
* @return The name of the file
|
||||
*/
|
||||
public String getDecryptedFileName() {
|
||||
File f = new File(getDecryptedRemotePath());
|
||||
return f.getName().length() == 0 ? ROOT_PATH : f.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encrypted filename and "/" for the root directory
|
||||
*
|
||||
* @return The name of the file
|
||||
*/
|
||||
public String getEncryptedFileName() {
|
||||
File f = new File(remotePath);
|
||||
return f.getName().length() == 0 ? ROOT_PATH : f.getName();
|
||||
}
|
||||
|
@ -386,6 +424,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
private void resetData() {
|
||||
fileId = -1;
|
||||
remotePath = null;
|
||||
decryptedRemotePath = null;
|
||||
parentId = 0;
|
||||
localPath = null;
|
||||
mimeType = null;
|
||||
|
@ -408,7 +447,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
sharedWithSharee = false;
|
||||
favorite = false;
|
||||
encrypted = false;
|
||||
encryptedFileName = null;
|
||||
mountType = WebdavEntry.MountType.INTERNAL;
|
||||
richWorkspace = "";
|
||||
}
|
||||
|
@ -464,8 +502,16 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
public String toString() {
|
||||
String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, " +
|
||||
"parentId=%s, etag=%s, favourite=%s]";
|
||||
return String.format(asString, fileId, getFileName(), mimeType, isDown(), localPath, remotePath, parentId,
|
||||
etag, favorite);
|
||||
return String.format(asString,
|
||||
fileId,
|
||||
getFileName(),
|
||||
mimeType,
|
||||
isDown(),
|
||||
localPath,
|
||||
remotePath,
|
||||
parentId,
|
||||
etag,
|
||||
favorite);
|
||||
}
|
||||
|
||||
public void setEtag(String etag) {
|
||||
|
@ -652,10 +698,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
return this.richWorkspace;
|
||||
}
|
||||
|
||||
public String getEncryptedFileName() {
|
||||
return this.encryptedFileName;
|
||||
}
|
||||
|
||||
public void setFileId(long fileId) {
|
||||
this.fileId = fileId;
|
||||
}
|
||||
|
@ -767,8 +809,4 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
|||
public void setRichWorkspace(String richWorkspace) {
|
||||
this.richWorkspace = richWorkspace;
|
||||
}
|
||||
|
||||
public void setEncryptedFileName(String encryptedFileName) {
|
||||
this.encryptedFileName = encryptedFileName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -332,7 +332,7 @@ public final class ThumbnailsCacheManager {
|
|||
GetMethod getMethod = null;
|
||||
try {
|
||||
String uri = mClient.getBaseUri() + "/index.php/core/preview.png?file="
|
||||
+ URLEncoder.encode(file.getRemotePath())
|
||||
+ URLEncoder.encode(file.getRemotePath())
|
||||
+ "&x=" + pxW + "&y=" + pxH + "&a=1&mode=cover&forceIcon=0";
|
||||
getMethod = new GetMethod(uri);
|
||||
|
||||
|
@ -619,7 +619,7 @@ public final class ThumbnailsCacheManager {
|
|||
String uri;
|
||||
if (file instanceof OCFile) {
|
||||
uri = mClient.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" +
|
||||
pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/");
|
||||
pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/");
|
||||
} else {
|
||||
uri = mClient.getBaseUri() + "/index.php/apps/files_trashbin/preview?fileId=" +
|
||||
file.getLocalId() + "&x=" + pxW + "&y=" + pxH;
|
||||
|
|
|
@ -31,7 +31,7 @@ import com.owncloud.android.MainApp;
|
|||
*/
|
||||
public class ProviderMeta {
|
||||
public static final String DB_NAME = "filelist";
|
||||
public static final int DB_VERSION = 55;
|
||||
public static final int DB_VERSION = 56;
|
||||
|
||||
private ProviderMeta() {
|
||||
// No instance
|
||||
|
@ -89,6 +89,7 @@ public class ProviderMeta {
|
|||
public static final String FILE_CONTENT_TYPE = "content_type";
|
||||
public static final String FILE_STORAGE_PATH = "media_path";
|
||||
public static final String FILE_PATH = "path";
|
||||
public static final String FILE_PATH_DECRYPTED = "path_decrypted";
|
||||
public static final String FILE_ACCOUNT_OWNER = "file_owner";
|
||||
public static final String FILE_LAST_SYNC_DATE = "last_sync_date";// _for_properties, but let's keep it as it is
|
||||
public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";
|
||||
|
|
|
@ -280,7 +280,7 @@ public class FileMenuFilter {
|
|||
List<Integer> toHide,
|
||||
OCCapability capability
|
||||
) {
|
||||
if (deviceInfo.editorSupported()) {
|
||||
if (deviceInfo.editorSupported() || files.iterator().next().isEncrypted()) {
|
||||
toHide.add(R.id.action_edit);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -292,13 +292,13 @@ public class FileDownloader extends Service
|
|||
*/
|
||||
public void cancel(Account account, OCFile file) {
|
||||
Pair<DownloadFileOperation, String> removeResult =
|
||||
mPendingDownloads.remove(account.name, file.getRemotePath());
|
||||
mPendingDownloads.remove(account.name, file.getRemotePath());
|
||||
DownloadFileOperation download = removeResult.first;
|
||||
if (download != null) {
|
||||
download.cancel();
|
||||
} else {
|
||||
if (mCurrentDownload != null && mCurrentAccount != null &&
|
||||
mCurrentDownload.getRemotePath().startsWith(file.getRemotePath()) &&
|
||||
mCurrentDownload.getRemotePath().startsWith(file.getRemotePath()) &&
|
||||
account.name.equals(mCurrentAccount.name)) {
|
||||
mCurrentDownload.cancel();
|
||||
}
|
||||
|
|
|
@ -21,59 +21,246 @@
|
|||
|
||||
package com.owncloud.android.operations;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.DecryptedFolderMetadata;
|
||||
import com.owncloud.android.datamodel.EncryptedFolderMetadata;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||
import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.lib.resources.e2ee.ToggleEncryptionRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.ReadFolderRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.model.RemoteFile;
|
||||
import com.owncloud.android.operations.common.SyncOperation;
|
||||
import com.owncloud.android.utils.EncryptionUtils;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
import com.owncloud.android.utils.MimeType;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR;
|
||||
import static com.owncloud.android.datamodel.OCFile.ROOT_PATH;
|
||||
|
||||
|
||||
/**
|
||||
* Access to remote operation performing the creation of a new folder in the ownCloud server.
|
||||
* Save the new folder in Database
|
||||
* Access to remote operation performing the creation of a new folder in the ownCloud server. Save the new folder in
|
||||
* Database
|
||||
*/
|
||||
public class CreateFolderOperation extends SyncOperation implements OnRemoteOperationListener{
|
||||
public class CreateFolderOperation extends SyncOperation implements OnRemoteOperationListener {
|
||||
|
||||
private static final String TAG = CreateFolderOperation.class.getSimpleName();
|
||||
|
||||
protected String mRemotePath;
|
||||
private boolean mCreateFullPath;
|
||||
protected String remotePath;
|
||||
private RemoteFile createdRemoteFolder;
|
||||
private Account account;
|
||||
private Context context;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param createFullPath 'True' means that all the ancestor folders should be created
|
||||
* if don't exist yet.
|
||||
*/
|
||||
public CreateFolderOperation(String remotePath, boolean createFullPath) {
|
||||
mRemotePath = remotePath;
|
||||
mCreateFullPath = createFullPath;
|
||||
public CreateFolderOperation(String remotePath, Account account, Context context) {
|
||||
this.remotePath = remotePath;
|
||||
this.account = account;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected RemoteOperationResult run(OwnCloudClient client) {
|
||||
RemoteOperationResult result = new CreateFolderRemoteOperation(mRemotePath, mCreateFullPath).execute(client);
|
||||
String remoteParentPath = new File(getRemotePath()).getParent();
|
||||
remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ?
|
||||
remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR;
|
||||
|
||||
OCFile parent = getStorageManager().getFileByDecryptedRemotePath(remoteParentPath);
|
||||
|
||||
// check if any parent is encrypted
|
||||
boolean encryptedAncestor = FileStorageUtils.checkEncryptionStatus(parent, getStorageManager());
|
||||
|
||||
if (encryptedAncestor) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
return encryptedCreate(parent, remoteParentPath, client);
|
||||
} else {
|
||||
Log_OC.e(TAG, "Encrypted upload on old Android API");
|
||||
return new RemoteOperationResult(RemoteOperationResult.ResultCode.OLD_ANDROID_API);
|
||||
}
|
||||
} else {
|
||||
return normalCreate(client);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||
private RemoteOperationResult encryptedCreate(OCFile parent, String remoteParentPath, OwnCloudClient client) {
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
|
||||
String privateKey = arbitraryDataProvider.getValue(account.name, EncryptionUtils.PRIVATE_KEY);
|
||||
String publicKey = arbitraryDataProvider.getValue(account.name, EncryptionUtils.PUBLIC_KEY);
|
||||
|
||||
String token = null;
|
||||
Boolean metadataExists;
|
||||
DecryptedFolderMetadata metadata;
|
||||
String encryptedRemotePath = null;
|
||||
|
||||
String filename = new File(remotePath).getName();
|
||||
|
||||
try {
|
||||
// lock folder
|
||||
token = EncryptionUtils.lockFolder(parent, client);
|
||||
|
||||
// get metadata
|
||||
Pair<Boolean, DecryptedFolderMetadata> metadataPair = EncryptionUtils.retrieveMetadata(parent,
|
||||
client,
|
||||
privateKey,
|
||||
publicKey);
|
||||
|
||||
metadataExists = metadataPair.first;
|
||||
metadata = metadataPair.second;
|
||||
|
||||
// check if filename already exists
|
||||
for (String key : metadata.getFiles().keySet()) {
|
||||
DecryptedFolderMetadata.DecryptedFile file = metadata.getFiles().get(key);
|
||||
|
||||
if (file != null && filename.equalsIgnoreCase(file.getEncrypted().getFilename())) {
|
||||
return new RemoteOperationResult(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
// generate new random file name, check if it exists in metadata
|
||||
String encryptedFileName = UUID.randomUUID().toString().replaceAll("-", "");
|
||||
|
||||
while (metadata.getFiles().get(encryptedFileName) != null) {
|
||||
encryptedFileName = UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
encryptedRemotePath = parent.getRemotePath() + encryptedFileName;
|
||||
|
||||
RemoteOperationResult result = new CreateFolderRemoteOperation(encryptedRemotePath,
|
||||
true,
|
||||
token)
|
||||
.execute(client);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
RemoteOperationResult remoteFolderOperationResult = new ReadFolderRemoteOperation(encryptedRemotePath)
|
||||
.execute(client);
|
||||
|
||||
createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0);
|
||||
OCFile newDir = new OCFile(createdRemoteFolder.getRemotePath());
|
||||
newDir.setMimeType(MimeType.DIRECTORY);
|
||||
|
||||
newDir.setParentId(parent.getFileId());
|
||||
newDir.setRemoteId(createdRemoteFolder.getRemoteId());
|
||||
newDir.setModificationTimestamp(System.currentTimeMillis());
|
||||
newDir.setEncrypted(true);
|
||||
newDir.setPermissions(createdRemoteFolder.getPermissions());
|
||||
newDir.setDecryptedRemotePath(parent.getDecryptedRemotePath() + filename + "/");
|
||||
getStorageManager().saveFile(newDir);
|
||||
|
||||
RemoteOperationResult encryptionOperationResult = new ToggleEncryptionRemoteOperation(
|
||||
newDir.getLocalId(),
|
||||
newDir.getRemotePath(),
|
||||
true)
|
||||
.execute(client);
|
||||
|
||||
if (!encryptionOperationResult.isSuccess()) {
|
||||
throw new RuntimeException("Error creating encrypted subfolder!");
|
||||
}
|
||||
|
||||
// Key, always generate new one
|
||||
byte[] key = EncryptionUtils.generateKey();
|
||||
|
||||
// IV, always generate new one
|
||||
byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength);
|
||||
|
||||
// update metadata
|
||||
DecryptedFolderMetadata.DecryptedFile decryptedFile = new DecryptedFolderMetadata.DecryptedFile();
|
||||
DecryptedFolderMetadata.Data data = new DecryptedFolderMetadata.Data();
|
||||
data.setFilename(filename);
|
||||
data.setMimetype(MimeType.WEBDAV_FOLDER);
|
||||
data.setKey(EncryptionUtils.encodeBytesToBase64String(key));
|
||||
|
||||
decryptedFile.setEncrypted(data);
|
||||
decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(iv));
|
||||
|
||||
metadata.getFiles().put(encryptedFileName, decryptedFile);
|
||||
|
||||
EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.encryptFolderMetadata(metadata,
|
||||
privateKey);
|
||||
String serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata);
|
||||
|
||||
// upload metadata
|
||||
EncryptionUtils.uploadMetadata(parent,
|
||||
serializedFolderMetadata,
|
||||
token,
|
||||
client,
|
||||
metadataExists);
|
||||
} else {
|
||||
// revert to sane state in case of any error
|
||||
Log_OC.e(TAG, remotePath + " hasn't been created");
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
// // revert to latest metadata
|
||||
// try {
|
||||
// Pair<Boolean, DecryptedFolderMetadata> metadataPair = EncryptionUtils.retrieveMetadata(parent,
|
||||
// client,
|
||||
// privateKey,
|
||||
// publicKey);
|
||||
// } catch (Exception metadataException) {
|
||||
// throw new RuntimeException(metadataException);
|
||||
// }
|
||||
|
||||
if (!EncryptionUtils.unlockFolder(parent, client, token).isSuccess()) {
|
||||
throw new RuntimeException("Could not clean up after failing folder creation!");
|
||||
}
|
||||
|
||||
// remove folder
|
||||
if (encryptedRemotePath != null) {
|
||||
RemoteOperationResult removeResult = new RemoveRemoteEncryptedFileOperation(encryptedRemotePath,
|
||||
parent.getLocalId(),
|
||||
account,
|
||||
context,
|
||||
filename).execute(client);
|
||||
|
||||
if (!removeResult.isSuccess()) {
|
||||
throw new RuntimeException("Could not clean up after failing folder creation!");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO do better
|
||||
return new RemoteOperationResult(e);
|
||||
} finally {
|
||||
|
||||
// unlock folder
|
||||
if (token != null) {
|
||||
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolder(parent, client, token);
|
||||
|
||||
if (!unlockFolderResult.isSuccess()) {
|
||||
// TODO do better
|
||||
throw new RuntimeException("Could not unlock folder!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteOperationResult normalCreate(OwnCloudClient client) {
|
||||
RemoteOperationResult result = new CreateFolderRemoteOperation(remotePath, true).execute(client);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
RemoteOperationResult remoteFolderOperationResult = new ReadFolderRemoteOperation(mRemotePath)
|
||||
RemoteOperationResult remoteFolderOperationResult = new ReadFolderRemoteOperation(remotePath)
|
||||
.execute(client);
|
||||
|
||||
createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0);
|
||||
saveFolderInDB();
|
||||
} else {
|
||||
Log_OC.e(TAG, mRemotePath + " hasn't been created");
|
||||
Log_OC.e(TAG, remotePath + " hasn't been created");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -87,36 +274,34 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper
|
|||
}
|
||||
|
||||
private void onCreateRemoteFolderOperationFinish(RemoteOperationResult result) {
|
||||
if (result.isSuccess()) {
|
||||
saveFolderInDB();
|
||||
} else {
|
||||
Log_OC.e(TAG, mRemotePath + " hasn't been created");
|
||||
}
|
||||
if (result.isSuccess()) {
|
||||
saveFolderInDB();
|
||||
} else {
|
||||
Log_OC.e(TAG, remotePath + " hasn't been created");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save new directory in local database.
|
||||
*/
|
||||
private void saveFolderInDB() {
|
||||
if (mCreateFullPath && getStorageManager().
|
||||
getFileByPath(FileStorageUtils.getParentPath(mRemotePath)) == null){// When parent
|
||||
// of remote path
|
||||
// is not created
|
||||
String[] subFolders = mRemotePath.split(PATH_SEPARATOR);
|
||||
if (getStorageManager().getFileByPath(FileStorageUtils.getParentPath(remotePath)) == null) {
|
||||
// When parent of remote path is not created
|
||||
String[] subFolders = remotePath.split(PATH_SEPARATOR);
|
||||
String composedRemotePath = ROOT_PATH;
|
||||
|
||||
// For each ancestor folders create them recursively
|
||||
for (String subFolder : subFolders) {
|
||||
if (!subFolder.isEmpty()) {
|
||||
composedRemotePath = composedRemotePath + subFolder + PATH_SEPARATOR;
|
||||
mRemotePath = composedRemotePath;
|
||||
remotePath = composedRemotePath;
|
||||
saveFolderInDB();
|
||||
}
|
||||
}
|
||||
} else { // Create directory on DB
|
||||
OCFile newDir = new OCFile(mRemotePath);
|
||||
OCFile newDir = new OCFile(remotePath);
|
||||
newDir.setMimeType(MimeType.DIRECTORY);
|
||||
long parentId = getStorageManager().getFileByPath(FileStorageUtils.getParentPath(mRemotePath)).getFileId();
|
||||
long parentId = getStorageManager().getFileByPath(FileStorageUtils.getParentPath(remotePath)).getFileId();
|
||||
newDir.setParentId(parentId);
|
||||
newDir.setRemoteId(createdRemoteFolder.getRemoteId());
|
||||
newDir.setModificationTimestamp(System.currentTimeMillis());
|
||||
|
@ -124,11 +309,11 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper
|
|||
newDir.setPermissions(createdRemoteFolder.getPermissions());
|
||||
getStorageManager().saveFile(newDir);
|
||||
|
||||
Log_OC.d(TAG, "Create directory " + mRemotePath + " in Database");
|
||||
Log_OC.d(TAG, "Create directory " + remotePath + " in Database");
|
||||
}
|
||||
}
|
||||
|
||||
public String getRemotePath() {
|
||||
return mRemotePath;
|
||||
return remotePath;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,8 +87,12 @@ public class DownloadFileOperation extends RemoteOperation {
|
|||
|
||||
public String getSavePath() {
|
||||
if (file.getStoragePath() != null) {
|
||||
File parentFile = new File(file.getStoragePath()).getParentFile();
|
||||
if (parentFile != null && !parentFile.exists()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
File path = new File(file.getStoragePath()); // re-downloads should be done over the original file
|
||||
if (path.canWrite()) {
|
||||
if (path.canWrite() || parentFile != null && parentFile.canWrite()) {
|
||||
return path.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +121,7 @@ public class DownloadFileOperation extends RemoteOperation {
|
|||
file.getRemotePath().lastIndexOf('.') + 1));
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " +
|
||||
file.getRemotePath());
|
||||
file.getRemotePath());
|
||||
}
|
||||
}
|
||||
if (mimeType == null) {
|
||||
|
|
|
@ -46,6 +46,7 @@ import com.owncloud.android.syncadapter.FileSyncAdapter;
|
|||
import com.owncloud.android.utils.DataHolderUtil;
|
||||
import com.owncloud.android.utils.EncryptionUtils;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
import com.owncloud.android.utils.MimeType;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -57,6 +58,8 @@ import java.util.Vector;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR;
|
||||
|
||||
|
||||
/**
|
||||
* Remote operation performing the synchronization of the list of files contained
|
||||
|
@ -241,7 +244,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|||
|
||||
if (!mSyncFullAccount) {
|
||||
sendLocalBroadcast(
|
||||
EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result
|
||||
EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -251,7 +254,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|||
|
||||
if (!mSyncFullAccount) {
|
||||
sendLocalBroadcast(
|
||||
EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result
|
||||
EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -422,7 +425,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|||
// update permission
|
||||
mLocalFolder.setPermissions(remoteFolder.getPermissions());
|
||||
|
||||
// update richWorkpace
|
||||
// update richWorkspace
|
||||
mLocalFolder.setRichWorkspace(remoteFolder.getRichWorkspace());
|
||||
|
||||
DecryptedFolderMetadata metadata = getDecryptedFolderMetadata(encryptedAncestor);
|
||||
|
@ -475,6 +478,10 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|||
}
|
||||
|
||||
// save updated contents in local database
|
||||
// update file name for encrypted files
|
||||
if (metadata != null) {
|
||||
updateFileNameForEncryptedFile(metadata, mLocalFolder);
|
||||
}
|
||||
mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
|
||||
|
||||
mChildren = updatedFiles;
|
||||
|
@ -492,15 +499,25 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|||
}
|
||||
|
||||
private void updateFileNameForEncryptedFile(@NonNull DecryptedFolderMetadata metadata, OCFile updatedFile) {
|
||||
updatedFile.setEncryptedFileName(updatedFile.getFileName());
|
||||
try {
|
||||
String decryptedFileName = metadata.getFiles().get(updatedFile.getFileName()).getEncrypted()
|
||||
.getFilename();
|
||||
String mimetype = metadata.getFiles().get(updatedFile.getFileName()).getEncrypted().getMimetype();
|
||||
updatedFile.setFileName(decryptedFileName);
|
||||
|
||||
OCFile parentFile = mStorageManager.getFileById(updatedFile.getParentId());
|
||||
String decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName;
|
||||
|
||||
if (updatedFile.isFolder()) {
|
||||
decryptedRemotePath += "/";
|
||||
}
|
||||
updatedFile.setDecryptedRemotePath(decryptedRemotePath);
|
||||
|
||||
if (mimetype == null || mimetype.isEmpty()) {
|
||||
updatedFile.setMimeType("application/octet-stream");
|
||||
if (updatedFile.isFolder()) {
|
||||
updatedFile.setMimeType(MimeType.DIRECTORY);
|
||||
} else {
|
||||
updatedFile.setMimeType("application/octet-stream");
|
||||
}
|
||||
} else {
|
||||
updatedFile.setMimeType(mimetype);
|
||||
}
|
||||
|
@ -516,7 +533,11 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|||
updatedFile.setModificationTimestampAtLastSyncForData(
|
||||
localFile.getModificationTimestampAtLastSyncForData()
|
||||
);
|
||||
updatedFile.setStoragePath(localFile.getStoragePath());
|
||||
if (localFile.isEncrypted()) {
|
||||
updatedFile.setStoragePath(mLocalFolder.getRemotePath() + PATH_SEPARATOR + localFile.getFileName());
|
||||
} else {
|
||||
updatedFile.setStoragePath(localFile.getStoragePath());
|
||||
}
|
||||
|
||||
// eTag will not be updated unless file CONTENTS are synchronized
|
||||
if (!updatedFile.isFolder() && localFile.isDown() &&
|
||||
|
@ -555,8 +576,11 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|||
for (OCFile file : localFiles) {
|
||||
String remotePath = file.getRemotePath();
|
||||
|
||||
if (metadata != null && !file.isFolder()) {
|
||||
if (metadata != null) {
|
||||
remotePath = file.getParentRemotePath() + file.getEncryptedFileName();
|
||||
if (file.isFolder() && !remotePath.endsWith(PATH_SEPARATOR)) {
|
||||
remotePath = remotePath + PATH_SEPARATOR;
|
||||
}
|
||||
}
|
||||
localFilesMap.put(remotePath, file);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ import com.owncloud.android.utils.MimeTypeUtil;
|
|||
public class RemoveFileOperation extends SyncOperation {
|
||||
|
||||
private OCFile fileToRemove;
|
||||
private String remotePath;
|
||||
private boolean onlyLocalCopy;
|
||||
private Account account;
|
||||
private boolean inBackground;
|
||||
|
@ -52,14 +51,15 @@ public class RemoveFileOperation extends SyncOperation {
|
|||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param remotePath RemotePath of the OCFile instance describing the remote file or
|
||||
* folder to remove from the server
|
||||
* @param onlyLocalCopy When 'true', and a local copy of the file exists, only this is
|
||||
* removed.
|
||||
* @param fileToRemove OCFile instance describing the remote file or folder to remove from the server
|
||||
* @param onlyLocalCopy When 'true', and a local copy of the file exists, only this is removed.
|
||||
*/
|
||||
public RemoveFileOperation(String remotePath, boolean onlyLocalCopy, Account account, boolean inBackground,
|
||||
public RemoveFileOperation(OCFile fileToRemove,
|
||||
boolean onlyLocalCopy,
|
||||
Account account,
|
||||
boolean inBackground,
|
||||
Context context) {
|
||||
this.remotePath = remotePath;
|
||||
this.fileToRemove = fileToRemove;
|
||||
this.onlyLocalCopy = onlyLocalCopy;
|
||||
this.account = account;
|
||||
this.inBackground = inBackground;
|
||||
|
@ -90,8 +90,6 @@ public class RemoveFileOperation extends SyncOperation {
|
|||
RemoteOperationResult result = null;
|
||||
RemoteOperation operation;
|
||||
|
||||
fileToRemove = getStorageManager().getFileByPath(remotePath);
|
||||
|
||||
if (MimeTypeUtil.isImage(fileToRemove.getMimeType())) {
|
||||
// store resized image
|
||||
ThumbnailsCacheManager.generateResizedImage(fileToRemove);
|
||||
|
@ -99,20 +97,21 @@ public class RemoveFileOperation extends SyncOperation {
|
|||
|
||||
boolean localRemovalFailed = false;
|
||||
if (!onlyLocalCopy) {
|
||||
|
||||
if (fileToRemove.isEncrypted() &&
|
||||
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
||||
OCFile parent = getStorageManager().getFileByPath(fileToRemove.getParentRemotePath());
|
||||
operation = new RemoveRemoteEncryptedFileOperation(remotePath, parent.getLocalId(), account, context,
|
||||
fileToRemove.getEncryptedFileName());
|
||||
operation = new RemoveRemoteEncryptedFileOperation(fileToRemove.getRemotePath(),
|
||||
parent.getLocalId(),
|
||||
account,
|
||||
context,
|
||||
fileToRemove.getEncryptedFileName());
|
||||
} else {
|
||||
operation = new RemoveFileRemoteOperation(remotePath);
|
||||
operation = new RemoveFileRemoteOperation(fileToRemove.getDecryptedRemotePath());
|
||||
}
|
||||
result = operation.execute(client);
|
||||
if (result.isSuccess() || result.getCode() == ResultCode.FILE_NOT_FOUND) {
|
||||
localRemovalFailed = !(getStorageManager().removeFile(fileToRemove, true, true));
|
||||
}
|
||||
|
||||
} else {
|
||||
localRemovalFailed = !(getStorageManager().removeFile(fileToRemove, false, true));
|
||||
if (!localRemovalFailed) {
|
||||
|
@ -126,5 +125,4 @@ public class RemoveFileOperation extends SyncOperation {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,8 +41,19 @@ import com.owncloud.android.lib.resources.e2ee.UpdateMetadataRemoteOperation;
|
|||
import com.owncloud.android.utils.EncryptionUtils;
|
||||
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.httpclient.NameValuePair;
|
||||
import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
/**
|
||||
|
@ -68,7 +79,10 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
|
|||
* @param remotePath RemotePath of the remote file or folder to remove from the server
|
||||
* @param parentId local id of parent folder
|
||||
*/
|
||||
RemoveRemoteEncryptedFileOperation(String remotePath, String parentId, Account account, Context context,
|
||||
RemoveRemoteEncryptedFileOperation(String remotePath,
|
||||
String parentId,
|
||||
Account account,
|
||||
Context context,
|
||||
String fileName) {
|
||||
this.remotePath = remotePath;
|
||||
this.parentId = parentId;
|
||||
|
@ -90,8 +104,6 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
|
|||
|
||||
String privateKey = arbitraryDataProvider.getValue(account.name, EncryptionUtils.PRIVATE_KEY);
|
||||
|
||||
// unlock
|
||||
|
||||
try {
|
||||
// Lock folder
|
||||
RemoteOperationResult lockFileOperationResult = new LockFileRemoteOperation(parentId).execute(client);
|
||||
|
@ -122,6 +134,7 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
|
|||
|
||||
// delete file remote
|
||||
delete = new DeleteMethod(client.getWebdavUri() + WebdavUtils.encodePath(remotePath));
|
||||
delete.setQueryString(new NameValuePair[]{new NameValuePair(E2E_TOKEN, token)});
|
||||
int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT);
|
||||
|
||||
delete.getResponseBodyAsString(); // exhaust the response, although not interesting
|
||||
|
@ -136,8 +149,9 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
|
|||
String serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata);
|
||||
|
||||
// upload metadata
|
||||
RemoteOperationResult uploadMetadataOperationResult = new UpdateMetadataRemoteOperation(parentId,
|
||||
serializedFolderMetadata, token).execute(client);
|
||||
RemoteOperationResult uploadMetadataOperationResult =
|
||||
new UpdateMetadataRemoteOperation(parentId,
|
||||
serializedFolderMetadata, token).execute(client);
|
||||
|
||||
if (!uploadMetadataOperationResult.isSuccess()) {
|
||||
throw new RemoteOperationFailedException("Metadata not uploaded!");
|
||||
|
@ -145,7 +159,14 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
|
|||
|
||||
// return success
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
} catch (NoSuchAlgorithmException |
|
||||
IOException |
|
||||
InvalidKeyException |
|
||||
InvalidAlgorithmParameterException |
|
||||
NoSuchPaddingException |
|
||||
BadPaddingException |
|
||||
IllegalBlockSizeException |
|
||||
InvalidKeySpecException e) {
|
||||
result = new RemoteOperationResult(e);
|
||||
Log_OC.e(TAG, "Remove " + remotePath + ": " + result.getLogMessage(), e);
|
||||
|
||||
|
@ -167,5 +188,4 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -90,8 +90,11 @@ public class RenameFileOperation extends SyncOperation {
|
|||
return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE);
|
||||
}
|
||||
|
||||
result = new RenameFileRemoteOperation(file.getFileName(), file.getRemotePath(), newName,
|
||||
file.isFolder()).execute(client);
|
||||
result = new RenameFileRemoteOperation(file.getFileName(),
|
||||
file.getRemotePath(),
|
||||
newName,
|
||||
file.isFolder())
|
||||
.execute(client);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
if (file.isFolder()) {
|
||||
|
@ -104,7 +107,7 @@ public class RenameFileOperation extends SyncOperation {
|
|||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "Rename " + file.getRemotePath() + " to " + ((newRemotePath ==null) ?
|
||||
Log_OC.e(TAG, "Rename " + file.getRemotePath() + " to " + ((newRemotePath == null) ?
|
||||
newName : newRemotePath) + ": " +
|
||||
(result!= null ? result.getLogMessage() : ""), e);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
package com.owncloud.android.operations;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
|
|
|
@ -20,14 +20,14 @@
|
|||
|
||||
package com.owncloud.android.operations;
|
||||
|
||||
class UploadException extends Exception {
|
||||
public class UploadException extends Exception {
|
||||
private static final long serialVersionUID = 5931153844211429915L;
|
||||
|
||||
UploadException() {
|
||||
public UploadException() {
|
||||
super();
|
||||
}
|
||||
|
||||
UploadException(String message) {
|
||||
public UploadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ import android.net.Uri;
|
|||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.nextcloud.client.device.BatteryStatus;
|
||||
import com.nextcloud.client.device.PowerManagementService;
|
||||
import com.nextcloud.client.network.Connectivity;
|
||||
|
@ -51,11 +51,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperation;
|
|||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.lib.resources.e2ee.GetMetadataRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.e2ee.LockFileRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.e2ee.StoreMetadataRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.e2ee.UnlockFileRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.e2ee.UpdateMetadataRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.ChunkedFileUploadRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
|
||||
|
@ -84,7 +80,6 @@ import java.io.RandomAccessFile;
|
|||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.channels.OverlappingFileLockException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
@ -459,52 +454,19 @@ public class UploadFileOperation extends SyncOperation {
|
|||
}
|
||||
/***** E2E *****/
|
||||
|
||||
// Lock folder
|
||||
LockFileRemoteOperation lockFileOperation = new LockFileRemoteOperation(parentFile.getLocalId());
|
||||
RemoteOperationResult lockFileOperationResult = lockFileOperation.execute(client);
|
||||
|
||||
if (lockFileOperationResult.isSuccess()) {
|
||||
token = (String) lockFileOperationResult.getData().get(0);
|
||||
// immediately store it
|
||||
mUpload.setFolderUnlockToken(token);
|
||||
uploadsStorageManager.updateUpload(mUpload);
|
||||
} else if (lockFileOperationResult.getHttpCode() == HttpStatus.SC_FORBIDDEN) {
|
||||
throw new UploadException("Forbidden! Please try again later.)");
|
||||
} else {
|
||||
throw new UploadException("Unknown error!");
|
||||
}
|
||||
token = EncryptionUtils.lockFolder(parentFile, client);
|
||||
// immediately store it
|
||||
mUpload.setFolderUnlockToken(token);
|
||||
uploadsStorageManager.updateUpload(mUpload);
|
||||
|
||||
// Update metadata
|
||||
GetMetadataRemoteOperation getMetadataOperation = new GetMetadataRemoteOperation(parentFile.getLocalId());
|
||||
RemoteOperationResult getMetadataOperationResult = getMetadataOperation.execute(client);
|
||||
Pair<Boolean, DecryptedFolderMetadata> metadataPair = EncryptionUtils.retrieveMetadata(parentFile,
|
||||
client,
|
||||
privateKey,
|
||||
publicKey);
|
||||
|
||||
DecryptedFolderMetadata metadata;
|
||||
|
||||
if (getMetadataOperationResult.isSuccess()) {
|
||||
metadataExists = true;
|
||||
|
||||
// decrypt metadata
|
||||
String serializedEncryptedMetadata = (String) getMetadataOperationResult.getData().get(0);
|
||||
|
||||
|
||||
EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.deserializeJSON(
|
||||
serializedEncryptedMetadata, new TypeToken<EncryptedFolderMetadata>() {
|
||||
});
|
||||
|
||||
metadata = EncryptionUtils.decryptFolderMetaData(encryptedFolderMetadata, privateKey);
|
||||
|
||||
} else if (getMetadataOperationResult.getHttpCode() == HttpStatus.SC_NOT_FOUND) {
|
||||
// new metadata
|
||||
metadata = new DecryptedFolderMetadata();
|
||||
metadata.setMetadata(new DecryptedFolderMetadata.Metadata());
|
||||
metadata.getMetadata().setMetadataKeys(new HashMap<>());
|
||||
String metadataKey = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey());
|
||||
String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(metadataKey, publicKey);
|
||||
metadata.getMetadata().getMetadataKeys().put(0, encryptedMetadataKey);
|
||||
} else {
|
||||
// TODO error
|
||||
throw new UploadException("something wrong");
|
||||
}
|
||||
metadataExists = metadataPair.first;
|
||||
DecryptedFolderMetadata metadata = metadataPair.second;
|
||||
|
||||
/**** E2E *****/
|
||||
|
||||
|
@ -544,8 +506,6 @@ public class UploadFileOperation extends SyncOperation {
|
|||
encryptedFileName = UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
|
||||
mFile.setEncryptedFileName(encryptedFileName);
|
||||
|
||||
File encryptedTempFile = File.createTempFile("encFile", encryptedFileName);
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(encryptedTempFile);
|
||||
fileOutputStream.write(encryptedFile.encryptedBytes);
|
||||
|
@ -561,7 +521,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
// this basically means that the file is on SD card
|
||||
// try to copy file to temporary dir if it doesn't exist
|
||||
String temporalPath = FileStorageUtils.getInternalTemporalPath(mAccount.name, mContext) +
|
||||
mFile.getRemotePath();
|
||||
mFile.getRemotePath();
|
||||
mFile.setStoragePath(temporalPath);
|
||||
temporalFile = new File(temporalPath);
|
||||
|
||||
|
@ -598,12 +558,18 @@ public class UploadFileOperation extends SyncOperation {
|
|||
|
||||
mUploadOperation = new ChunkedFileUploadRemoteOperation(encryptedTempFile.getAbsolutePath(),
|
||||
mFile.getParentRemotePath() + encryptedFileName,
|
||||
mFile.getMimeType(), mFile.getEtagInConflict(),
|
||||
timeStamp, onWifiConnection);
|
||||
mFile.getMimeType(),
|
||||
mFile.getEtagInConflict(),
|
||||
timeStamp,
|
||||
onWifiConnection,
|
||||
token);
|
||||
} else {
|
||||
mUploadOperation = new UploadFileRemoteOperation(encryptedTempFile.getAbsolutePath(),
|
||||
mFile.getParentRemotePath() + encryptedFileName, mFile.getMimeType(),
|
||||
mFile.getEtagInConflict(), timeStamp);
|
||||
mFile.getParentRemotePath() + encryptedFileName,
|
||||
mFile.getMimeType(),
|
||||
mFile.getEtagInConflict(),
|
||||
timeStamp,
|
||||
token);
|
||||
}
|
||||
|
||||
for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) {
|
||||
|
@ -623,10 +589,13 @@ public class UploadFileOperation extends SyncOperation {
|
|||
}
|
||||
|
||||
if (result.isSuccess()) {
|
||||
// upload metadata
|
||||
mFile.setDecryptedRemotePath(parentFile.getDecryptedRemotePath() + originalFile.getName());
|
||||
mFile.setRemotePath(parentFile.getRemotePath() + encryptedFileName);
|
||||
|
||||
// update metadata
|
||||
DecryptedFolderMetadata.DecryptedFile decryptedFile = new DecryptedFolderMetadata.DecryptedFile();
|
||||
DecryptedFolderMetadata.Data data = new DecryptedFolderMetadata.Data();
|
||||
data.setFilename(mFile.getFileName());
|
||||
data.setFilename(mFile.getDecryptedFileName());
|
||||
data.setMimetype(mFile.getMimeType());
|
||||
data.setKey(EncryptionUtils.encodeBytesToBase64String(key));
|
||||
|
||||
|
@ -641,22 +610,11 @@ public class UploadFileOperation extends SyncOperation {
|
|||
String serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata);
|
||||
|
||||
// upload metadata
|
||||
RemoteOperationResult uploadMetadataOperationResult;
|
||||
if (metadataExists) {
|
||||
// update metadata
|
||||
UpdateMetadataRemoteOperation storeMetadataOperation = new UpdateMetadataRemoteOperation(
|
||||
parentFile.getLocalId(), serializedFolderMetadata, token);
|
||||
uploadMetadataOperationResult = storeMetadataOperation.execute(client);
|
||||
} else {
|
||||
// store metadata
|
||||
StoreMetadataRemoteOperation storeMetadataOperation = new StoreMetadataRemoteOperation(
|
||||
parentFile.getLocalId(), serializedFolderMetadata);
|
||||
uploadMetadataOperationResult = storeMetadataOperation.execute(client);
|
||||
}
|
||||
|
||||
if (!uploadMetadataOperationResult.isSuccess()) {
|
||||
throw new UploadException();
|
||||
}
|
||||
EncryptionUtils.uploadMetadata(parentFile,
|
||||
serializedFolderMetadata,
|
||||
token,
|
||||
client,
|
||||
metadataExists);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore");
|
||||
|
@ -689,15 +647,20 @@ public class UploadFileOperation extends SyncOperation {
|
|||
|
||||
if (result.isSuccess()) {
|
||||
handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client);
|
||||
RemoteOperationResult unlockFolderResult = unlockFolder(parentFile, client, token);
|
||||
|
||||
if (!unlockFolderResult.isSuccess()) {
|
||||
return unlockFolderResult;
|
||||
}
|
||||
} else if (result.getCode() == ResultCode.SYNC_CONFLICT) {
|
||||
getStorageManager().saveConflict(mFile, mFile.getEtagInConflict());
|
||||
}
|
||||
|
||||
// unlock must be done always
|
||||
// TODO check if in good state
|
||||
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolder(parentFile,
|
||||
client,
|
||||
token);
|
||||
|
||||
if (!unlockFolderResult.isSuccess()) {
|
||||
return unlockFolderResult;
|
||||
}
|
||||
|
||||
// delete temporal file
|
||||
if (temporalFile != null && temporalFile.exists() && !temporalFile.delete()) {
|
||||
Log_OC.e(TAG, "Could not delete temporal file " + temporalFile.getAbsolutePath());
|
||||
|
@ -706,14 +669,6 @@ public class UploadFileOperation extends SyncOperation {
|
|||
return result;
|
||||
}
|
||||
|
||||
private RemoteOperationResult unlockFolder(OCFile parentFolder, OwnCloudClient client, String token) {
|
||||
if (token != null) {
|
||||
return new UnlockFileRemoteOperation(parentFolder.getLocalId(), token).execute(client);
|
||||
} else {
|
||||
return new RemoteOperationResult(new Exception("No token available"));
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteOperationResult checkConditions(File originalFile) {
|
||||
RemoteOperationResult remoteOperationResult = null;
|
||||
|
||||
|
@ -794,7 +749,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
// this basically means that the file is on SD card
|
||||
// try to copy file to temporary dir if it doesn't exist
|
||||
String temporalPath = FileStorageUtils.getInternalTemporalPath(mAccount.name, mContext) +
|
||||
mFile.getRemotePath();
|
||||
mFile.getRemotePath();
|
||||
mFile.setStoragePath(temporalPath);
|
||||
temporalFile = new File(temporalPath);
|
||||
|
||||
|
@ -830,12 +785,13 @@ public class UploadFileOperation extends SyncOperation {
|
|||
boolean onWifiConnection = connectivityService.getConnectivity().isWifi();
|
||||
|
||||
mUploadOperation = new ChunkedFileUploadRemoteOperation(mFile.getStoragePath(),
|
||||
mFile.getRemotePath(), mFile.getMimeType(),
|
||||
mFile.getRemotePath(),
|
||||
mFile.getMimeType(),
|
||||
mFile.getEtagInConflict(),
|
||||
timeStamp, onWifiConnection);
|
||||
} else {
|
||||
mUploadOperation = new UploadFileRemoteOperation(mFile.getStoragePath(),
|
||||
mFile.getRemotePath(), mFile.getMimeType(), mFile.getEtagInConflict(), timeStamp);
|
||||
mFile.getRemotePath(), mFile.getMimeType(), mFile.getEtagInConflict(), timeStamp);
|
||||
}
|
||||
|
||||
for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) {
|
||||
|
@ -1035,7 +991,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, false);
|
||||
RemoteOperationResult result = operation.execute(client);
|
||||
if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mRemoteFolderToBeCreated) {
|
||||
SyncOperation syncOp = new CreateFolderOperation(pathToGrant, true);
|
||||
SyncOperation syncOp = new CreateFolderOperation(pathToGrant, getAccount(), getContext());
|
||||
result = syncOp.execute(client, getStorageManager());
|
||||
}
|
||||
if (result.isSuccess()) {
|
||||
|
|
|
@ -472,7 +472,9 @@ public class DocumentsStorageProvider extends DocumentsProvider {
|
|||
String newDirPath = targetFolder.getRemotePath() + displayName + PATH_SEPARATOR;
|
||||
FileDataStorageManager storageManager = targetFolder.getStorageManager();
|
||||
|
||||
RemoteOperationResult result = new CreateFolderOperation(newDirPath, true)
|
||||
RemoteOperationResult result = new CreateFolderOperation(newDirPath,
|
||||
accountManager.getCurrentAccount(),
|
||||
getContext())
|
||||
.execute(targetFolder.getClient(), storageManager);
|
||||
|
||||
if (!result.isSuccess()) {
|
||||
|
@ -581,8 +583,12 @@ public class DocumentsStorageProvider extends DocumentsProvider {
|
|||
|
||||
recursiveRevokePermission(document);
|
||||
|
||||
RemoteOperationResult result = new RemoveFileOperation(document.getRemotePath(), false,
|
||||
document.getAccount(), true, context)
|
||||
OCFile file = document.getStorageManager().getFileByPath(document.getRemotePath());
|
||||
RemoteOperationResult result = new RemoveFileOperation(file,
|
||||
false,
|
||||
document.getAccount(),
|
||||
true,
|
||||
context)
|
||||
.execute(document.getClient(), document.getStorageManager());
|
||||
|
||||
if (!result.isSuccess()) {
|
||||
|
|
|
@ -692,6 +692,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
+ ProviderTableMeta.FILE_NAME + TEXT
|
||||
+ ProviderTableMeta.FILE_ENCRYPTED_NAME + TEXT
|
||||
+ ProviderTableMeta.FILE_PATH + TEXT
|
||||
+ ProviderTableMeta.FILE_PATH_DECRYPTED + TEXT
|
||||
+ ProviderTableMeta.FILE_PARENT + INTEGER
|
||||
+ ProviderTableMeta.FILE_CREATION + INTEGER
|
||||
+ ProviderTableMeta.FILE_MODIFIED + INTEGER
|
||||
|
@ -2193,6 +2194,25 @@ public class FileContentProvider extends ContentProvider {
|
|||
if (!upgraded) {
|
||||
Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
|
||||
}
|
||||
|
||||
if (oldVersion < 56 && newVersion >= 56) {
|
||||
Log_OC.i(SQL, "Entering in the #56 add decrypted remote path");
|
||||
db.beginTransaction();
|
||||
try {
|
||||
// Add synced.name_collision_policy
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
|
||||
ADD_COLUMN + ProviderTableMeta.FILE_PATH_DECRYPTED + " TEXT "); // strin
|
||||
|
||||
upgraded = true;
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
if (!upgraded) {
|
||||
Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,6 @@ public class OperationsService extends Service {
|
|||
public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
|
||||
public static final String EXTRA_NEWNAME = "NEWNAME";
|
||||
public static final String EXTRA_REMOVE_ONLY_LOCAL = "REMOVE_LOCAL_COPY";
|
||||
public static final String EXTRA_CREATE_FULL_PATH = "CREATE_FULL_PATH";
|
||||
public static final String EXTRA_SYNC_FILE_CONTENTS = "SYNC_FILE_CONTENTS";
|
||||
public static final String EXTRA_RESULT = "RESULT";
|
||||
public static final String EXTRA_NEW_PARENT_PATH = "NEW_PARENT_PATH";
|
||||
|
@ -636,17 +635,19 @@ public class OperationsService extends Service {
|
|||
|
||||
case ACTION_REMOVE:
|
||||
// Remove file or folder
|
||||
remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
|
||||
OCFile file = operationIntent.getParcelableExtra(EXTRA_FILE);
|
||||
boolean onlyLocalCopy = operationIntent.getBooleanExtra(EXTRA_REMOVE_ONLY_LOCAL, false);
|
||||
boolean inBackground = operationIntent.getBooleanExtra(EXTRA_IN_BACKGROUND, false);
|
||||
operation = new RemoveFileOperation(remotePath, onlyLocalCopy, account, inBackground,
|
||||
getApplicationContext());
|
||||
operation = new RemoveFileOperation(file,
|
||||
onlyLocalCopy,
|
||||
account,
|
||||
inBackground,
|
||||
getApplicationContext());
|
||||
break;
|
||||
|
||||
case ACTION_CREATE_FOLDER:
|
||||
remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
|
||||
boolean createFullPath = operationIntent.getBooleanExtra(EXTRA_CREATE_FULL_PATH, true);
|
||||
operation = new CreateFolderOperation(remotePath, createFullPath);
|
||||
operation = new CreateFolderOperation(remotePath, account, getApplicationContext());
|
||||
break;
|
||||
|
||||
case ACTION_SYNC_FILE:
|
||||
|
|
|
@ -151,14 +151,14 @@ class SyncFolderHandler extends Handler {
|
|||
return;
|
||||
}
|
||||
Pair<SynchronizeFolderOperation, String> removeResult = mPendingOperations.remove(account.name,
|
||||
file.getRemotePath());
|
||||
file.getRemotePath());
|
||||
SynchronizeFolderOperation synchronization = removeResult.first;
|
||||
if (synchronization != null) {
|
||||
synchronization.cancel();
|
||||
} else {
|
||||
// TODO synchronize?
|
||||
if (mCurrentSyncOperation != null && mCurrentAccount != null &&
|
||||
mCurrentSyncOperation.getRemotePath().startsWith(file.getRemotePath()) &&
|
||||
mCurrentSyncOperation.getRemotePath().startsWith(file.getRemotePath()) &&
|
||||
account.name.equals(mCurrentAccount.name)) {
|
||||
mCurrentSyncOperation.cancel();
|
||||
}
|
||||
|
|
|
@ -347,7 +347,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
|||
|
||||
if (mCancellation && i <files.size()) {
|
||||
Log_OC.d(TAG,
|
||||
"Leaving synchronization before synchronizing " + files.get(i).getRemotePath() +
|
||||
"Leaving synchronization before synchronizing " + files.get(i).getRemotePath() +
|
||||
" due to cancellation request");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -710,12 +710,12 @@ public class FileDisplayActivity extends FileActivity
|
|||
boolean success) {
|
||||
FileFragment secondFragment = getSecondFragment();
|
||||
boolean waitedPreview = mWaitingToPreview != null
|
||||
&& mWaitingToPreview.getRemotePath().equals(downloadedRemotePath);
|
||||
&& mWaitingToPreview.getRemotePath().equals(downloadedRemotePath);
|
||||
if (secondFragment instanceof FileDetailFragment) {
|
||||
FileDetailFragment detailsFragment = (FileDetailFragment) secondFragment;
|
||||
OCFile fileInFragment = detailsFragment.getFile();
|
||||
if (fileInFragment != null &&
|
||||
!downloadedRemotePath.equals(fileInFragment.getRemotePath())) {
|
||||
!downloadedRemotePath.equals(fileInFragment.getRemotePath())) {
|
||||
// the user browsed to other file ; forget the automatic preview
|
||||
mWaitingToPreview = null;
|
||||
|
||||
|
@ -1326,9 +1326,9 @@ public class FileDisplayActivity extends FileActivity
|
|||
|
||||
} else {
|
||||
OCFile currentFile = (getFile() == null) ? null :
|
||||
getStorageManager().getFileByPath(getFile().getRemotePath());
|
||||
getStorageManager().getFileByPath(getFile().getRemotePath());
|
||||
OCFile currentDir = (getCurrentDir() == null) ? null :
|
||||
getStorageManager().getFileByPath(getCurrentDir().getRemotePath());
|
||||
getStorageManager().getFileByPath(getCurrentDir().getRemotePath());
|
||||
|
||||
if (currentDir == null) {
|
||||
// current folder was removed from the server
|
||||
|
@ -1463,7 +1463,7 @@ public class FileDisplayActivity extends FileActivity
|
|||
boolean sameAccount = getAccount() != null && accountName.equals(getAccount().name);
|
||||
OCFile currentDir = getCurrentDir();
|
||||
boolean isDescendant = currentDir != null && uploadedRemotePath != null &&
|
||||
uploadedRemotePath.startsWith(currentDir.getRemotePath());
|
||||
uploadedRemotePath.startsWith(currentDir.getRemotePath());
|
||||
|
||||
if (sameAccount && isDescendant) {
|
||||
String linkedToRemotePath =
|
||||
|
@ -1588,13 +1588,13 @@ public class FileDisplayActivity extends FileActivity
|
|||
OCFile currentDir = getCurrentDir();
|
||||
return currentDir != null &&
|
||||
downloadedRemotePath != null &&
|
||||
downloadedRemotePath.startsWith(currentDir.getRemotePath());
|
||||
downloadedRemotePath.startsWith(currentDir.getRemotePath());
|
||||
}
|
||||
|
||||
private boolean isAscendant(String linkedToRemotePath) {
|
||||
OCFile currentDir = getCurrentDir();
|
||||
return currentDir != null &&
|
||||
currentDir.getRemotePath().startsWith(linkedToRemotePath);
|
||||
currentDir.getRemotePath().startsWith(linkedToRemotePath);
|
||||
}
|
||||
|
||||
private boolean isSameAccount(Intent intent) {
|
||||
|
@ -2439,11 +2439,11 @@ public class FileDisplayActivity extends FileActivity
|
|||
public void cancelTransference(OCFile file) {
|
||||
getFileOperationsHelper().cancelTransference(file);
|
||||
if (mWaitingToPreview != null &&
|
||||
mWaitingToPreview.getRemotePath().equals(file.getRemotePath())) {
|
||||
mWaitingToPreview.getRemotePath().equals(file.getRemotePath())) {
|
||||
mWaitingToPreview = null;
|
||||
}
|
||||
if (mWaitingToSend != null &&
|
||||
mWaitingToSend.getRemotePath().equals(file.getRemotePath())) {
|
||||
mWaitingToSend.getRemotePath().equals(file.getRemotePath())) {
|
||||
mWaitingToSend = null;
|
||||
}
|
||||
onTransferStateChanged(file, false, false);
|
||||
|
|
|
@ -38,7 +38,6 @@ import android.content.res.ColorStateList;
|
|||
import android.content.res.Resources.NotFoundException;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
@ -1106,9 +1105,9 @@ public class ReceiveExternalFilesActivity extends FileActivity
|
|||
|
||||
} else {
|
||||
OCFile currentFile = (mFile == null) ? null :
|
||||
getStorageManager().getFileByPath(mFile.getRemotePath());
|
||||
getStorageManager().getFileByPath(mFile.getRemotePath());
|
||||
OCFile currentDir = (getCurrentFolder() == null) ? null :
|
||||
getStorageManager().getFileByPath(getCurrentFolder().getRemotePath());
|
||||
getStorageManager().getFileByPath(getCurrentFolder().getRemotePath());
|
||||
|
||||
if (currentDir == null) {
|
||||
// current folder was removed from the server
|
||||
|
|
|
@ -258,7 +258,10 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||
int filesSize = mFiles.size();
|
||||
for (int i = 0; i < filesSize; i++) {
|
||||
if (mFiles.get(i).getRemoteId().equals(fileId)) {
|
||||
mFiles.get(i).setEncrypted(encrypted);
|
||||
OCFile file = mFiles.get(i);
|
||||
file.setEncrypted(encrypted);
|
||||
mStorageManager.saveFile(file);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -555,7 +558,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||
if (holder instanceof OCFileListGridItemViewHolder) {
|
||||
OCFileListGridItemViewHolder gridItemViewHolder = (OCFileListGridItemViewHolder) holder;
|
||||
|
||||
gridItemViewHolder.fileName.setText(file.getFileName());
|
||||
gridItemViewHolder.fileName.setText(file.getDecryptedFileName());
|
||||
|
||||
if (gridView && gridImage) {
|
||||
gridItemViewHolder.fileName.setVisibility(View.GONE);
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
package com.owncloud.android.ui.dialog;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
|
@ -48,9 +47,7 @@ import com.owncloud.android.datamodel.OCFile;
|
|||
import com.owncloud.android.datamodel.Template;
|
||||
import com.owncloud.android.files.CreateFileFromTemplateOperation;
|
||||
import com.owncloud.android.files.FetchTemplateOperation;
|
||||
import com.owncloud.android.lib.common.OwnCloudAccount;
|
||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
|
||||
|
|
|
@ -137,8 +137,8 @@ public class CreateFolderDialogFragment
|
|||
return;
|
||||
}
|
||||
|
||||
String path = mParentFolder.getRemotePath() + newFolderName + OCFile.PATH_SEPARATOR;
|
||||
((ComponentsGetter) getActivity()).getFileOperationsHelper().createFolder(path, false);
|
||||
String path = mParentFolder.getDecryptedRemotePath() + newFolderName + OCFile.PATH_SEPARATOR;
|
||||
((ComponentsGetter) getActivity()).getFileOperationsHelper().createFolder(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -236,7 +236,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
|
|||
return dialog;
|
||||
}
|
||||
|
||||
private class DownloadKeysAsyncTask extends AsyncTask<Void, Void, String> {
|
||||
public class DownloadKeysAsyncTask extends AsyncTask<Void, Void, String> {
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
|
@ -316,7 +316,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
|
|||
}
|
||||
}
|
||||
|
||||
private class GenerateNewKeysAsyncTask extends AsyncTask<Void, Void, String> {
|
||||
public class GenerateNewKeysAsyncTask extends AsyncTask<Void, Void, String> {
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
|
|
|
@ -607,7 +607,7 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
|
|||
} else {
|
||||
// Get public share
|
||||
publicShare = fileDataStorageManager.getFirstShareByPathAndType(file.getRemotePath(),
|
||||
ShareType.PUBLIC_LINK, "");
|
||||
ShareType.PUBLIC_LINK, "");
|
||||
|
||||
// Update public share section
|
||||
updatePublicShareSection();
|
||||
|
|
|
@ -125,16 +125,20 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog {
|
|||
ThemeUtils.getDefaultDisplayNameForRootFolder(getContext())));
|
||||
|
||||
OCCapability capability = fileActivity.getCapabilities();
|
||||
if (capability.getRichDocuments().isTrue() && capability.getRichDocumentsDirectEditing().isTrue() &&
|
||||
if (capability.getRichDocuments().isTrue() &&
|
||||
capability.getRichDocumentsDirectEditing().isTrue() &&
|
||||
android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
|
||||
capability.getRichDocumentsTemplatesAvailable().isTrue()) {
|
||||
capability.getRichDocumentsTemplatesAvailable().isTrue() &&
|
||||
!file.isEncrypted()) {
|
||||
templates.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
String json = new ArbitraryDataProvider(getContext().getContentResolver())
|
||||
.getValue(user.toPlatformAccount(), ArbitraryDataProvider.DIRECT_EDITING);
|
||||
|
||||
if (!json.isEmpty() && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (!json.isEmpty() &&
|
||||
android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
|
||||
!file.isEncrypted()) {
|
||||
DirectEditing directEditing = new Gson().fromJson(json, DirectEditing.class);
|
||||
|
||||
if (!directEditing.getCreators().isEmpty()) {
|
||||
|
@ -174,7 +178,7 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog {
|
|||
FileMenuFilter.isEditorAvailable(getContext().getContentResolver(),
|
||||
user,
|
||||
MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN) &&
|
||||
file != null) {
|
||||
file != null && !file.isEncrypted()) {
|
||||
// richWorkspace
|
||||
// == "": no info set -> show button
|
||||
// == null: disabled on server side -> hide button
|
||||
|
|
|
@ -1008,17 +1008,18 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
|||
.getCapability(account.getAccountName());
|
||||
|
||||
if (PreviewMediaFragment.canBePreviewed(file) && account.getServer().getVersion()
|
||||
.isMediaStreamingSupported()) {
|
||||
.isMediaStreamingSupported() && !file.isEncrypted()) {
|
||||
// stream media preview on >= NC14
|
||||
((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true, true, true);
|
||||
} else if (FileMenuFilter.isEditorAvailable(requireContext().getContentResolver(),
|
||||
accountManager.getUser(),
|
||||
file.getMimeType()) &&
|
||||
!file.isEncrypted() &&
|
||||
android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
mContainerActivity.getFileOperationsHelper().openFileWithTextEditor(file, getContext());
|
||||
} else if (capability.getRichDocumentsMimeTypeList().contains(file.getMimeType()) &&
|
||||
android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
|
||||
capability.getRichDocumentsDirectEditing().isTrue()) {
|
||||
capability.getRichDocumentsDirectEditing().isTrue() && !file.isEncrypted()) {
|
||||
mContainerActivity.getFileOperationsHelper().openFileAsRichDocument(file, getContext());
|
||||
} else {
|
||||
// automatic download, preview on finish
|
||||
|
|
|
@ -160,8 +160,8 @@ public class SearchShareesFragment extends Fragment implements ShareUserListAdap
|
|||
// Get Users and Groups
|
||||
if (((FileActivity) mListener).getStorageManager() != null) {
|
||||
mShares = ((FileActivity) mListener).getStorageManager().getSharesWithForAFile(
|
||||
mFile.getRemotePath(),
|
||||
mAccount.name
|
||||
mFile.getRemotePath(),
|
||||
mAccount.name
|
||||
);
|
||||
|
||||
// Update list of users/groups
|
||||
|
|
|
@ -606,8 +606,8 @@ public class ShareFileFragment extends Fragment implements ShareUserListAdapter.
|
|||
if (((FileActivity) mListener).getStorageManager() != null) {
|
||||
// Get Users and Groups
|
||||
mPrivateShares = ((FileActivity) mListener).getStorageManager().getSharesWithForAFile(
|
||||
mFile.getRemotePath(),
|
||||
mAccount.name
|
||||
mFile.getRemotePath(),
|
||||
mAccount.name
|
||||
);
|
||||
|
||||
// Update list of users/groups
|
||||
|
@ -672,9 +672,9 @@ public class ShareFileFragment extends Fragment implements ShareUserListAdapter.
|
|||
} else if (((FileActivity) mListener).getStorageManager() != null) {
|
||||
// Get public share
|
||||
mPublicShare = ((FileActivity) mListener).getStorageManager().getFirstShareByPathAndType(
|
||||
mFile.getRemotePath(),
|
||||
ShareType.PUBLIC_LINK,
|
||||
""
|
||||
mFile.getRemotePath(),
|
||||
ShareType.PUBLIC_LINK,
|
||||
""
|
||||
);
|
||||
|
||||
// Update public share section
|
||||
|
|
|
@ -227,7 +227,7 @@ public class FileOperationsHelper {
|
|||
|
||||
// check for changed eTag
|
||||
CheckEtagRemoteOperation checkEtagOperation = new CheckEtagRemoteOperation(file.getRemotePath(),
|
||||
file.getEtag());
|
||||
file.getEtag());
|
||||
RemoteOperationResult result = checkEtagOperation.execute(user.toPlatformAccount(), fileActivity);
|
||||
|
||||
// eTag changed, sync file
|
||||
|
@ -766,7 +766,7 @@ public class FileOperationsHelper {
|
|||
sendIntent.setComponent(new ComponentName(packageName, activityName));
|
||||
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" +
|
||||
context.getResources().getString(R.string.image_cache_provider_authority) +
|
||||
file.getRemotePath()));
|
||||
file.getRemotePath()));
|
||||
sendIntent.putExtra(Intent.ACTION_SEND, true); // Send Action
|
||||
sendIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
|
@ -797,7 +797,7 @@ public class FileOperationsHelper {
|
|||
} else {
|
||||
uri = Uri.parse(UriUtils.URI_CONTENT_SCHEME +
|
||||
context.getResources().getString(R.string.image_cache_provider_authority) +
|
||||
file.getRemotePath());
|
||||
file.getRemotePath());
|
||||
}
|
||||
|
||||
intent.setDataAndType(uri, file.getMimeType());
|
||||
|
@ -863,7 +863,7 @@ public class FileOperationsHelper {
|
|||
public void toggleEncryption(OCFile file, boolean shouldBeEncrypted) {
|
||||
if (file.isEncrypted() != shouldBeEncrypted) {
|
||||
EventBus.getDefault().post(new EncryptionEvent(file.getLocalId(), file.getRemoteId(), file.getRemotePath(),
|
||||
shouldBeEncrypted));
|
||||
shouldBeEncrypted));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -894,7 +894,7 @@ public class FileOperationsHelper {
|
|||
Intent service = new Intent(fileActivity, OperationsService.class);
|
||||
service.setAction(OperationsService.ACTION_REMOVE);
|
||||
service.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
|
||||
service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
|
||||
service.putExtra(OperationsService.EXTRA_FILE, file);
|
||||
service.putExtra(OperationsService.EXTRA_REMOVE_ONLY_LOCAL, onlyLocalCopy);
|
||||
service.putExtra(OperationsService.EXTRA_IN_BACKGROUND, inBackground);
|
||||
mWaitingForOpId = fileActivity.getOperationsServiceBinder().queueNewOperation(service);
|
||||
|
@ -906,13 +906,12 @@ public class FileOperationsHelper {
|
|||
}
|
||||
|
||||
|
||||
public void createFolder(String remotePath, boolean createFullPath) {
|
||||
public void createFolder(String remotePath) {
|
||||
// Create Folder
|
||||
Intent service = new Intent(fileActivity, OperationsService.class);
|
||||
service.setAction(OperationsService.ACTION_CREATE_FOLDER);
|
||||
service.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
|
||||
service.putExtra(OperationsService.EXTRA_REMOTE_PATH, remotePath);
|
||||
service.putExtra(OperationsService.EXTRA_CREATE_FULL_PATH, createFullPath);
|
||||
mWaitingForOpId = fileActivity.getOperationsServiceBinder().queueNewOperation(service);
|
||||
|
||||
fileActivity.showLoadingDialog(fileActivity.getString(R.string.wait_a_moment));
|
||||
|
|
|
@ -53,7 +53,6 @@ import java.util.List;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
|
|
@ -24,7 +24,9 @@ package com.owncloud.android.utils;
|
|||
import android.accounts.Account;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
@ -36,8 +38,14 @@ import com.owncloud.android.lib.common.OwnCloudClient;
|
|||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.lib.resources.e2ee.GetMetadataRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.e2ee.LockFileRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.e2ee.StoreMetadataRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.e2ee.UnlockFileRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.e2ee.UpdateMetadataRemoteOperation;
|
||||
import com.owncloud.android.operations.UploadException;
|
||||
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -97,9 +105,9 @@ public final class EncryptionUtils {
|
|||
public static final String MNEMONIC = "MNEMONIC";
|
||||
public static final int ivLength = 16;
|
||||
public static final int saltLength = 40;
|
||||
public static final String ivDelimiter = "|"; // not base64 encoded
|
||||
|
||||
private static final String HASH_DELIMITER = "$";
|
||||
private static final String ivDelimiter = "fA=="; // "|" base64 encoded
|
||||
private static final int iterationCount = 1024;
|
||||
private static final int keyStrength = 256;
|
||||
private static final String AES_CIPHER = "AES/GCM/NoPadding";
|
||||
|
@ -539,7 +547,7 @@ public final class EncryptionUtils {
|
|||
IllegalBlockSizeException, InvalidKeySpecException, InvalidAlgorithmParameterException {
|
||||
|
||||
// split up iv, salt
|
||||
String[] strings = privateKey.split(ivDelimiter);
|
||||
String[] strings = privateKey.split("\\" + ivDelimiter);
|
||||
String realPrivateKey = strings[0];
|
||||
byte[] iv = decodeStringToBase64Bytes(strings[1]);
|
||||
byte[] salt = decodeStringToBase64Bytes(strings[2]);
|
||||
|
@ -704,4 +712,93 @@ public final class EncryptionUtils {
|
|||
|
||||
return hashWithSalt.equals(newHash);
|
||||
}
|
||||
|
||||
public static String lockFolder(OCFile parentFile, OwnCloudClient client) throws UploadException {
|
||||
// Lock folder
|
||||
LockFileRemoteOperation lockFileOperation = new LockFileRemoteOperation(parentFile.getLocalId());
|
||||
RemoteOperationResult lockFileOperationResult = lockFileOperation.execute(client);
|
||||
|
||||
if (lockFileOperationResult.isSuccess() &&
|
||||
!TextUtils.isEmpty((String) lockFileOperationResult.getData().get(0))) {
|
||||
return (String) lockFileOperationResult.getData().get(0);
|
||||
} else if (lockFileOperationResult.getHttpCode() == HttpStatus.SC_FORBIDDEN) {
|
||||
throw new UploadException("Forbidden! Please try again later.)");
|
||||
} else {
|
||||
throw new UploadException("Could not lock folder");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parentFile file metadata should be retrieved for
|
||||
* @return Pair: boolean: true: metadata already exists, false: metadata new created
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||
public static Pair<Boolean, DecryptedFolderMetadata> retrieveMetadata(OCFile parentFile,
|
||||
OwnCloudClient client,
|
||||
String privateKey,
|
||||
String publicKey) throws UploadException,
|
||||
InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException,
|
||||
IllegalBlockSizeException, InvalidKeyException, InvalidKeySpecException, CertificateException {
|
||||
GetMetadataRemoteOperation getMetadataOperation = new GetMetadataRemoteOperation(parentFile.getLocalId());
|
||||
RemoteOperationResult getMetadataOperationResult = getMetadataOperation.execute(client);
|
||||
|
||||
DecryptedFolderMetadata metadata;
|
||||
|
||||
if (getMetadataOperationResult.isSuccess()) {
|
||||
// decrypt metadata
|
||||
String serializedEncryptedMetadata = (String) getMetadataOperationResult.getData().get(0);
|
||||
|
||||
|
||||
EncryptedFolderMetadata encryptedFolderMetadata = EncryptionUtils.deserializeJSON(
|
||||
serializedEncryptedMetadata, new TypeToken<EncryptedFolderMetadata>() {
|
||||
});
|
||||
|
||||
return new Pair<>(Boolean.TRUE, EncryptionUtils.decryptFolderMetaData(encryptedFolderMetadata, privateKey));
|
||||
|
||||
} else if (getMetadataOperationResult.getHttpCode() == HttpStatus.SC_NOT_FOUND) {
|
||||
// new metadata
|
||||
metadata = new DecryptedFolderMetadata();
|
||||
metadata.setMetadata(new DecryptedFolderMetadata.Metadata());
|
||||
metadata.getMetadata().setMetadataKeys(new HashMap<>());
|
||||
String metadataKey = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey());
|
||||
String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(metadataKey, publicKey);
|
||||
metadata.getMetadata().getMetadataKeys().put(0, encryptedMetadataKey);
|
||||
|
||||
return new Pair<>(Boolean.FALSE, metadata);
|
||||
} else {
|
||||
// TODO error
|
||||
throw new UploadException("something wrong");
|
||||
}
|
||||
}
|
||||
|
||||
public static void uploadMetadata(OCFile parentFile,
|
||||
String serializedFolderMetadata,
|
||||
String token,
|
||||
OwnCloudClient client,
|
||||
boolean metadataExists) throws UploadException {
|
||||
RemoteOperationResult uploadMetadataOperationResult;
|
||||
if (metadataExists) {
|
||||
// update metadata
|
||||
UpdateMetadataRemoteOperation storeMetadataOperation = new UpdateMetadataRemoteOperation(
|
||||
parentFile.getLocalId(), serializedFolderMetadata, token);
|
||||
uploadMetadataOperationResult = storeMetadataOperation.execute(client);
|
||||
} else {
|
||||
// store metadata
|
||||
StoreMetadataRemoteOperation storeMetadataOperation = new StoreMetadataRemoteOperation(
|
||||
parentFile.getLocalId(), serializedFolderMetadata);
|
||||
uploadMetadataOperationResult = storeMetadataOperation.execute(client);
|
||||
}
|
||||
|
||||
if (!uploadMetadataOperationResult.isSuccess()) {
|
||||
throw new UploadException("Storing/updating metadata was not successful");
|
||||
}
|
||||
}
|
||||
|
||||
public static RemoteOperationResult unlockFolder(OCFile parentFolder, OwnCloudClient client, String token) {
|
||||
if (token != null) {
|
||||
return new UnlockFileRemoteOperation(parentFolder.getLocalId(), token).execute(client);
|
||||
} else {
|
||||
return new RemoteOperationResult(new Exception("No token available"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,6 +201,7 @@ public final class FileStorageUtils {
|
|||
*/
|
||||
public static OCFile fillOCFile(RemoteFile remote) {
|
||||
OCFile file = new OCFile(remote.getRemotePath());
|
||||
file.setDecryptedRemotePath(remote.getRemotePath());
|
||||
file.setCreationTimestamp(remote.getCreationTimestamp());
|
||||
if (MimeType.DIRECTORY.equalsIgnoreCase(remote.getMimeType())) {
|
||||
file.setFileLength(remote.getSize());
|
||||
|
@ -449,7 +450,7 @@ public final class FileStorageUtils {
|
|||
return true;
|
||||
}
|
||||
|
||||
while (!OCFile.ROOT_PATH.equals(file.getRemotePath())) {
|
||||
while (!OCFile.ROOT_PATH.equals(file.getDecryptedRemotePath())) {
|
||||
if (file.isEncrypted()) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -93,4 +93,4 @@ public class GetShareWithUsersAsyncTask extends AsyncTask<Object, Void, Pair<Rem
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,11 @@ package com.owncloud.android.utils;
|
|||
*/
|
||||
public final class MimeType {
|
||||
public static final String DIRECTORY = "DIR";
|
||||
public static final String WEBDAV_FOLDER = "httpd/unix-directory";
|
||||
public static final String JPEG = "image/jpeg";
|
||||
public static final String TIFF = "image/tiff";
|
||||
public static final String TEXT_PLAIN = "text/plain";
|
||||
public static final String FILE = "application/octet-stream";
|
||||
|
||||
private MimeType() {
|
||||
// No instance
|
||||
|
|
|
@ -301,7 +301,7 @@ public final class MimeTypeUtil {
|
|||
*/
|
||||
public static boolean isImage(ServerFileInterface file) {
|
||||
return MimeTypeUtil.isImage(file.getMimeType())
|
||||
|| MimeTypeUtil.isImage(getMimeTypeFromPath(file.getRemotePath()));
|
||||
|| MimeTypeUtil.isImage(getMimeTypeFromPath(file.getRemotePath()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -310,7 +310,7 @@ public final class MimeTypeUtil {
|
|||
*/
|
||||
public static boolean isText(OCFile file) {
|
||||
return MimeTypeUtil.isText(file.getMimeType())
|
||||
|| MimeTypeUtil.isText(getMimeTypeFromPath(file.getRemotePath()));
|
||||
|| MimeTypeUtil.isText(getMimeTypeFromPath(file.getRemotePath()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue