From a53d661d73b7b975a7515f33cd9298cfb1be333d Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 26 Apr 2024 13:07:54 +0200 Subject: [PATCH 01/16] Inform user for uploading same file again Signed-off-by: alperozturk --- .../nextcloud/client/jobs/upload/FileUploadWorker.kt | 3 +++ .../nextcloud/utils/extensions/ContextExtensions.kt | 11 +++++++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 15 insertions(+) diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt index 368f67227318..f0913b8fdffa 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt @@ -21,6 +21,8 @@ import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.model.WorkerState import com.nextcloud.model.WorkerStateLiveData +import com.nextcloud.utils.extensions.showToast +import com.owncloud.android.R import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.ThumbnailsCacheManager import com.owncloud.android.datamodel.UploadsStorageManager @@ -263,6 +265,7 @@ class FileUploadWorker( uploadFileOperation.user, File(uploadFileOperation.storagePath), uploadFileOperation.remotePath, context ) ) { + context.showToast(R.string.file_upload_worker_same_file_already_exists) return } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt index c032a63fcee9..dd4e4abc4e5b 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt @@ -13,6 +13,9 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.Build +import android.os.Handler +import android.os.Looper +import android.widget.Toast import com.owncloud.android.datamodel.ReceiverFlag @SuppressLint("UnspecifiedRegisterReceiverFlag") @@ -23,3 +26,11 @@ fun Context.registerBroadcastReceiver(receiver: BroadcastReceiver?, filter: Inte registerReceiver(receiver, filter) } } + +fun Context.showToast(message: String) { + Handler(Looper.getMainLooper()).post { + Toast.makeText(this, message, Toast.LENGTH_LONG).show() + } +} + +fun Context.showToast(messageId: Int) = showToast(getString(messageId)) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5d0045cfd9a5..80ed1b46b753 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -127,6 +127,7 @@ Keep file in source folder Delete file from source folder seconds ago + Same file already exists, no conflict detected LIVE No files here No folders here From b3357c0b07509d6bc22f0e4e7c87d875eff407b7 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 09:41:59 +0200 Subject: [PATCH 02/16] Extract logics to functions Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 151 ++++++++++-------- 1 file changed, 83 insertions(+), 68 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index b0d22cde137e..b283583c3ec9 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -77,6 +77,9 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -84,7 +87,10 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import javax.crypto.BadPaddingException; import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import androidx.annotation.CheckResult; import androidx.annotation.Nullable; @@ -435,6 +441,78 @@ protected RemoteOperationResult run(OwnCloudClient client) { } } + private String getToken(OwnCloudClient client, OCFile parentFile, long counter) throws UploadException { + String token; + if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) { + token = mFolderUnlockToken; + } else { + token = EncryptionUtils.lockFolder(parentFile, client, counter); + mUpload.setFolderUnlockToken(token); + uploadsStorageManager.updateUpload(mUpload); + } + + return token; + } + + private long getE2ECounter(OCFile parentFile) { + long counter = -1; + if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) { + counter = parentFile.getE2eCounter() + 1; + } + + return counter; + } + + private DecryptedFolderMetadataFileV1 getDecryptedFolderMetadataV1(String publicKey, Object object) + throws NoSuchPaddingException, IllegalBlockSizeException, CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + DecryptedFolderMetadataFileV1 metadata = new DecryptedFolderMetadataFileV1(); + metadata.setMetadata(new DecryptedMetadata()); + metadata.getMetadata().setVersion(1.2); + metadata.getMetadata().setMetadataKeys(new HashMap<>()); + String metadataKey = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey()); + String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(metadataKey, publicKey); + metadata.getMetadata().setMetadataKey(encryptedMetadataKey); + + if (object instanceof DecryptedFolderMetadataFileV1) { + metadata = (DecryptedFolderMetadataFileV1) object; + } + + return metadata; + } + + private List checkNameCollision(Object object) { + List fileNames = new ArrayList<>(); + + if (object instanceof DecryptedFolderMetadataFileV1 metadata) { + for (DecryptedFile file : metadata.getFiles().values()) { + fileNames.add(file.getEncrypted().getFilename()); + } + } else { + for (com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile file : + ((DecryptedFolderMetadataFile) object).getMetadata().getFiles().values()) { + fileNames.add(file.getFilename()); + } + } + + return fileNames; + } + + private String getEncryptedFileName(Object object) { + String encryptedFileName = EncryptionUtils.generateUid(); + + if (object instanceof DecryptedFolderMetadataFileV1 metadata) { + while (metadata.getFiles().get(encryptedFileName) != null) { + encryptedFileName = EncryptionUtils.generateUid(); + } + } else { + while (((DecryptedFolderMetadataFile) object).getMetadata().getFiles().get(encryptedFileName) != null) { + encryptedFileName = EncryptionUtils.generateUid(); + } + } + + return encryptedFileName; + } + // TODO REFACTOR @SuppressLint("AndroidLintUseSparseArrays") // gson cannot handle sparse arrays easily, therefore use hashmap private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile parentFile) { @@ -454,29 +532,14 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); try { - // check conditions result = checkConditions(originalFile); if (result != null) { return result; } - /***** E2E *****/ - // Only on V2+: whenever we change something, increase counter - long counter = -1; - if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) { - counter = parentFile.getE2eCounter() + 1; - } - - // we might have an old token from interrupted upload - if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) { - token = mFolderUnlockToken; - } else { - token = EncryptionUtils.lockFolder(parentFile, client, counter); - // immediately store it - mUpload.setFolderUnlockToken(token); - uploadsStorageManager.updateUpload(mUpload); - } + long counter = getE2ECounter(parentFile); + token = getToken(client, parentFile,counter); // Update metadata EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2(); @@ -487,46 +550,13 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) { if (object == null) { - // TODO return error return new RemoteOperationResult(new IllegalStateException("Metadata does not exist")); } } else { - // v1 is allowed to be null, thus create it - DecryptedFolderMetadataFileV1 metadata = new DecryptedFolderMetadataFileV1(); - metadata.setMetadata(new DecryptedMetadata()); - metadata.getMetadata().setVersion(1.2); - metadata.getMetadata().setMetadataKeys(new HashMap<>()); - String metadataKey = EncryptionUtils.encodeBytesToBase64String(EncryptionUtils.generateKey()); - String encryptedMetadataKey = EncryptionUtils.encryptStringAsymmetric(metadataKey, publicKey); - metadata.getMetadata().setMetadataKey(encryptedMetadataKey); - - if (object instanceof DecryptedFolderMetadataFileV1) { - metadata = (DecryptedFolderMetadataFileV1) object; - } - - object = metadata; + object = getDecryptedFolderMetadataV1(publicKey,object); } - // todo fail if no metadata - -// metadataExists = metadataPair.getFirst(); -// DecryptedFolderMetadataFile metadata = metadataPair.getSecond(); - - // TODO E2E: check counter: must be less than our counter, check rest: signature, etc - /**** E2E *****/ - - // check name collision - List fileNames = new ArrayList<>(); - if (object instanceof DecryptedFolderMetadataFileV1 metadata) { - for (DecryptedFile file : metadata.getFiles().values()) { - fileNames.add(file.getEncrypted().getFilename()); - } - } else { - for (com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile file : - ((DecryptedFolderMetadataFile) object).getMetadata().getFiles().values()) { - fileNames.add(file.getFilename()); - } - } + List fileNames = checkNameCollision(object); RemoteOperationResult collisionResult = checkNameCollision(client, fileNames, parentFile.isEncrypted()); if (collisionResult != null) { @@ -543,30 +573,15 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare return result; } - // Get the last modification date of the file from the file system long lastModifiedTimestamp = originalFile.lastModified() / 1000; - Long creationTimestamp = FileUtil.getCreationTimestamp(originalFile); - /***** E2E *****/ byte[] key = EncryptionUtils.generateKey(); byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength); Cipher cipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv); File file = new File(mFile.getStoragePath()); EncryptedFile encryptedFile = EncryptionUtils.encryptFile(user.getAccountName(), file, cipher); - - // new random file name, check if it exists in metadata - String encryptedFileName = EncryptionUtils.generateUid(); - - if (object instanceof DecryptedFolderMetadataFileV1 metadata) { - while (metadata.getFiles().get(encryptedFileName) != null) { - encryptedFileName = EncryptionUtils.generateUid(); - } - } else { - while (((DecryptedFolderMetadataFile) object).getMetadata().getFiles().get(encryptedFileName) != null) { - encryptedFileName = EncryptionUtils.generateUid(); - } - } + String encryptedFileName = getEncryptedFileName(object); encryptedTempFile = encryptedFile.getEncryptedFile(); From 2d8d0d9b5fd4ab1ddc382a186985ee11bf143c8f Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 09:53:13 +0200 Subject: [PATCH 03/16] Extract logics to functions Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 198 ++++++++++-------- 1 file changed, 112 insertions(+), 86 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index b283583c3ec9..bdb670d83771 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -77,6 +77,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; @@ -513,6 +514,108 @@ private String getEncryptedFileName(Object object) { return encryptedFileName; } + private void setUploadFileRemoteOperationForE2E(String token, File encryptedTempFile, String encryptedFileName, + long lastModifiedTimestamp, long creationTimestamp, long size) { + + if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { + boolean onWifiConnection = connectivityService.getConnectivity().isWifi(); + + mUploadOperation = new ChunkedFileUploadRemoteOperation(encryptedTempFile.getAbsolutePath(), + mFile.getParentRemotePath() + encryptedFileName, + mFile.getMimeType(), + mFile.getEtagInConflict(), + lastModifiedTimestamp, + onWifiConnection, + token, + creationTimestamp, + mDisableRetries + ); + } else { + mUploadOperation = new UploadFileRemoteOperation(encryptedTempFile.getAbsolutePath(), + mFile.getParentRemotePath() + encryptedFileName, + mFile.getMimeType(), + mFile.getEtagInConflict(), + lastModifiedTimestamp, + creationTimestamp, + token, + mDisableRetries + ); + } + } + + private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, byte[] key, byte[] iv, + EncryptedFile encryptedFile, String encryptedFileName, String publicKey, + OCFile parentFile, ArbitraryDataProvider arbitraryDataProvider, + String token, OwnCloudClient client, boolean metadataExists) + throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, + CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, + UploadException { + + DecryptedFile decryptedFile = new DecryptedFile(); + Data data = new Data(); + data.setFilename(mFile.getDecryptedFileName()); + data.setMimetype(mFile.getMimeType()); + data.setKey(EncryptionUtils.encodeBytesToBase64String(key)); + decryptedFile.setEncrypted(data); + decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(iv)); + decryptedFile.setAuthenticationTag(encryptedFile.getAuthenticationTag()); + + metadata.getFiles().put(encryptedFileName, decryptedFile); + + EncryptedFolderMetadataFileV1 encryptedFolderMetadata = + EncryptionUtils.encryptFolderMetadata(metadata, + publicKey, + parentFile.getLocalId(), + user, + arbitraryDataProvider + ); + + String serializedFolderMetadata; + + if (metadata.getMetadata().getMetadataKey() != null) { + serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata, true); + } else { + serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata); + } + + // upload metadata + EncryptionUtils.uploadMetadata(parentFile, + serializedFolderMetadata, + token, + client, + metadataExists, + E2EVersion.V1_2, + "", + arbitraryDataProvider, + user); + } + + + private void updateMetadataForV2(Object object, EncryptionUtilsV2 encryptionUtilsV2, String encryptedFileName, + byte[] key, byte[] iv, EncryptedFile encryptedFile, OCFile parentFile, String token, + OwnCloudClient client) throws UploadException { + + DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; + encryptionUtilsV2.addFileToMetadata( + encryptedFileName, + mFile, + iv, + encryptedFile.getAuthenticationTag(), + key, + metadata, + getStorageManager()); + + // upload metadata + encryptionUtilsV2.serializeAndUploadMetadata(parentFile, + metadata, + token, + client, + true, + mContext, + user, + getStorageManager()); + } + // TODO REFACTOR @SuppressLint("AndroidLintUseSparseArrays") // gson cannot handle sparse arrays easily, therefore use hashmap private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile parentFile) { @@ -539,7 +642,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } long counter = getE2ECounter(parentFile); - token = getToken(client, parentFile,counter); + token = getToken(client, parentFile, counter); // Update metadata EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2(); @@ -553,7 +656,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare return new RemoteOperationResult(new IllegalStateException("Metadata does not exist")); } } else { - object = getDecryptedFolderMetadataV1(publicKey,object); + object = getDecryptedFolderMetadataV1(publicKey, object); } List fileNames = checkNameCollision(object); @@ -617,32 +720,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } updateSize(size); - - /// perform the upload - if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { - boolean onWifiConnection = connectivityService.getConnectivity().isWifi(); - - mUploadOperation = new ChunkedFileUploadRemoteOperation(encryptedTempFile.getAbsolutePath(), - mFile.getParentRemotePath() + encryptedFileName, - mFile.getMimeType(), - mFile.getEtagInConflict(), - lastModifiedTimestamp, - onWifiConnection, - token, - creationTimestamp, - mDisableRetries - ); - } else { - mUploadOperation = new UploadFileRemoteOperation(encryptedTempFile.getAbsolutePath(), - mFile.getParentRemotePath() + encryptedFileName, - mFile.getMimeType(), - mFile.getEtagInConflict(), - lastModifiedTimestamp, - creationTimestamp, - token, - mDisableRetries - ); - } + setUploadFileRemoteOperationForE2E(token, encryptedTempFile, encryptedFileName, lastModifiedTimestamp, creationTimestamp, size); for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { mUploadOperation.addDataTransferProgressListener(mDataTransferListener); @@ -666,65 +744,13 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare if (object instanceof DecryptedFolderMetadataFileV1 metadata) { - // update metadata - DecryptedFile decryptedFile = new DecryptedFile(); - Data data = new Data(); - data.setFilename(mFile.getDecryptedFileName()); - data.setMimetype(mFile.getMimeType()); - data.setKey(EncryptionUtils.encodeBytesToBase64String(key)); - decryptedFile.setEncrypted(data); - decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(iv)); - decryptedFile.setAuthenticationTag(encryptedFile.getAuthenticationTag()); - - metadata.getFiles().put(encryptedFileName, decryptedFile); - - EncryptedFolderMetadataFileV1 encryptedFolderMetadata = - EncryptionUtils.encryptFolderMetadata(metadata, - publicKey, - parentFile.getLocalId(), - user, - arbitraryDataProvider - ); - - String serializedFolderMetadata; - - // check if we need metadataKeys - if (metadata.getMetadata().getMetadataKey() != null) { - serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata, true); - } else { - serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata); - } - - // upload metadata - EncryptionUtils.uploadMetadata(parentFile, - serializedFolderMetadata, - token, - client, - metadataExists, - E2EVersion.V1_2, - "", - arbitraryDataProvider, - user); + updateMetadataForV1(metadata, key, iv, encryptedFile, + encryptedFileName, publicKey, parentFile, + arbitraryDataProvider, token, client, + metadataExists); } else { - DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; - encryptionUtilsV2.addFileToMetadata( - encryptedFileName, - mFile, - iv, - encryptedFile.getAuthenticationTag(), - key, - metadata, - getStorageManager()); - - // upload metadata - encryptionUtilsV2.serializeAndUploadMetadata(parentFile, - metadata, - token, - client, - true, - mContext, - user, - getStorageManager()); + updateMetadataForV2(object,encryptionUtilsV2, encryptedFileName, key, + iv, encryptedFile, parentFile,token,client); } } } catch (FileNotFoundException e) { From a949bd515d5e3c22c29731f4171f30009b190f50 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 10:01:59 +0200 Subject: [PATCH 04/16] Extract logics to functions Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 102 ++++++++++-------- 1 file changed, 59 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index bdb670d83771..92caa2db18ab 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -15,6 +15,7 @@ import android.content.Intent; import android.net.Uri; import android.text.TextUtils; +import android.util.Pair; import com.nextcloud.client.account.User; import com.nextcloud.client.device.BatteryStatus; @@ -515,7 +516,7 @@ private String getEncryptedFileName(Object object) { } private void setUploadFileRemoteOperationForE2E(String token, File encryptedTempFile, String encryptedFileName, - long lastModifiedTimestamp, long creationTimestamp, long size) { + long lastModifiedTimestamp, long creationTimestamp, long size) { if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { boolean onWifiConnection = connectivityService.getConnectivity().isWifi(); @@ -592,8 +593,8 @@ private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, byte[] private void updateMetadataForV2(Object object, EncryptionUtilsV2 encryptionUtilsV2, String encryptedFileName, - byte[] key, byte[] iv, EncryptedFile encryptedFile, OCFile parentFile, String token, - OwnCloudClient client) throws UploadException { + byte[] key, byte[] iv, EncryptedFile encryptedFile, OCFile parentFile, String token, + OwnCloudClient client) throws UploadException { DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; encryptionUtilsV2.addFileToMetadata( @@ -749,8 +750,8 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare arbitraryDataProvider, token, client, metadataExists); } else { - updateMetadataForV2(object,encryptionUtilsV2, encryptedFileName, key, - iv, encryptedFile, parentFile,token,client); + updateMetadataForV2(object, encryptionUtilsV2, encryptedFileName, key, + iv, encryptedFile, parentFile, token, client); } } } catch (FileNotFoundException e) { @@ -762,56 +763,71 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } catch (Exception e) { result = new RemoteOperationResult(e); } finally { - mUploadStarted.set(false); - sendRefreshFolderEventBroadcast(); - - if (fileLock != null) { - try { - fileLock.release(); - } catch (IOException e) { - Log_OC.e(TAG, "Failed to unlock file with path " + mFile.getStoragePath()); - } - } - - if (temporalFile != null && !originalFile.equals(temporalFile)) { - temporalFile.delete(); - } - if (result == null) { - result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); - } - - logResult(result, mFile.getStoragePath(), mFile.getRemotePath()); - - // Unlock must be done otherwise folder stays locked and user can't upload any file - RemoteOperationResult unlockFolderResult; - if (object instanceof DecryptedFolderMetadataFileV1) { - unlockFolderResult = EncryptionUtils.unlockFolderV1(parentFile, client, token); - } else { - unlockFolderResult = EncryptionUtils.unlockFolder(parentFile, client, token); - } + result = cleanupE2EUpload(fileLock, temporalFile, originalFile, result, object, parentFile, client, token, encryptedTempFile); + } - if (unlockFolderResult != null && !unlockFolderResult.isSuccess()) { - result = unlockFolderResult; - } + completeE2EUpload(result, temporalFile, expectedFile, originalFile, client); - if (encryptedTempFile != null) { - boolean isTempEncryptedFileDeleted = encryptedTempFile.delete(); - Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted); - } else { - Log_OC.e(TAG, "Encrypted temp file cannot be found"); - } - } + return result; + } + private void completeE2EUpload(RemoteOperationResult result, File temporalFile, File expectedFile, File originalFile, OwnCloudClient client) { if (result.isSuccess()) { handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client); } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); } - // delete temporal file if (temporalFile != null && temporalFile.exists() && !temporalFile.delete()) { Log_OC.e(TAG, "Could not delete temporal file " + temporalFile.getAbsolutePath()); } + } + + private RemoteOperationResult cleanupE2EUpload( + FileLock fileLock, File temporalFile, File originalFile, + RemoteOperationResult result, Object object, + OCFile parentFile, OwnCloudClient client, + String token, File encryptedTempFile) { + + mUploadStarted.set(false); + sendRefreshFolderEventBroadcast(); + + if (fileLock != null) { + try { + fileLock.release(); + } catch (IOException e) { + Log_OC.e(TAG, "Failed to unlock file with path " + mFile.getStoragePath()); + } + } + + if (temporalFile != null && !originalFile.equals(temporalFile)) { + temporalFile.delete(); + } + + if (result == null) { + result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); + } + + logResult(result, mFile.getStoragePath(), mFile.getRemotePath()); + + // Unlock must be done otherwise folder stays locked and user can't upload any file + RemoteOperationResult unlockFolderResult; + if (object instanceof DecryptedFolderMetadataFileV1) { + unlockFolderResult = EncryptionUtils.unlockFolderV1(parentFile, client, token); + } else { + unlockFolderResult = EncryptionUtils.unlockFolder(parentFile, client, token); + } + + if (unlockFolderResult != null && !unlockFolderResult.isSuccess()) { + result = unlockFolderResult; + } + + if (encryptedTempFile != null) { + boolean isTempEncryptedFileDeleted = encryptedTempFile.delete(); + Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted); + } else { + Log_OC.e(TAG, "Encrypted temp file cannot be found"); + } return result; } From 90dc1c65359fd358ffd84d7595e3ba89d6877453 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 12:29:12 +0200 Subject: [PATCH 05/16] Use different data classes Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 136 +++++++++--------- .../android/operations/e2e/E2EClientData.kt | 12 ++ .../android/operations/e2e/E2EData.kt | 12 ++ .../android/operations/e2e/E2EFiles.kt | 19 +++ 4 files changed, 115 insertions(+), 64 deletions(-) create mode 100644 app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 92caa2db18ab..64d9bdcf0d80 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -53,6 +53,9 @@ import com.owncloud.android.lib.resources.files.model.RemoteFile; import com.owncloud.android.lib.resources.status.E2EVersion; import com.owncloud.android.operations.common.SyncOperation; +import com.owncloud.android.operations.e2e.E2EClientData; +import com.owncloud.android.operations.e2e.E2EData; +import com.owncloud.android.operations.e2e.E2EFiles; import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.EncryptionUtilsV2; import com.owncloud.android.utils.FileStorageUtils; @@ -544,28 +547,26 @@ private void setUploadFileRemoteOperationForE2E(String token, File encryptedTemp } } - private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, byte[] key, byte[] iv, - EncryptedFile encryptedFile, String encryptedFileName, String publicKey, - OCFile parentFile, ArbitraryDataProvider arbitraryDataProvider, - String token, OwnCloudClient client, boolean metadataExists) + private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, E2EData e2eData, E2EClientData clientData, String encryptedFileName, + OCFile parentFile, ArbitraryDataProvider arbitraryDataProvider, boolean metadataExists) + throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, - CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, - UploadException { + CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, UploadException { DecryptedFile decryptedFile = new DecryptedFile(); Data data = new Data(); data.setFilename(mFile.getDecryptedFileName()); data.setMimetype(mFile.getMimeType()); - data.setKey(EncryptionUtils.encodeBytesToBase64String(key)); + data.setKey(EncryptionUtils.encodeBytesToBase64String(e2eData.getKey())); decryptedFile.setEncrypted(data); - decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(iv)); - decryptedFile.setAuthenticationTag(encryptedFile.getAuthenticationTag()); + decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(e2eData.getIv())); + decryptedFile.setAuthenticationTag(e2eData.getEncryptedFile().getAuthenticationTag()); metadata.getFiles().put(encryptedFileName, decryptedFile); EncryptedFolderMetadataFileV1 encryptedFolderMetadata = EncryptionUtils.encryptFolderMetadata(metadata, - publicKey, + clientData.getPublicKey(), parentFile.getLocalId(), user, arbitraryDataProvider @@ -582,8 +583,8 @@ private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, byte[] // upload metadata EncryptionUtils.uploadMetadata(parentFile, serializedFolderMetadata, - token, - client, + clientData.getToken(), + clientData.getClient(), metadataExists, E2EVersion.V1_2, "", @@ -593,24 +594,23 @@ private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, byte[] private void updateMetadataForV2(Object object, EncryptionUtilsV2 encryptionUtilsV2, String encryptedFileName, - byte[] key, byte[] iv, EncryptedFile encryptedFile, OCFile parentFile, String token, - OwnCloudClient client) throws UploadException { + E2EData e2eData, E2EClientData clientData, OCFile parentFile) throws UploadException { DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; encryptionUtilsV2.addFileToMetadata( encryptedFileName, mFile, - iv, - encryptedFile.getAuthenticationTag(), - key, + e2eData.getIv(), + e2eData.getEncryptedFile().getAuthenticationTag(), + e2eData.getKey(), metadata, getStorageManager()); // upload metadata encryptionUtilsV2.serializeAndUploadMetadata(parentFile, metadata, - token, - client, + clientData.getToken(), + clientData.getClient(), true, mContext, user, @@ -621,10 +621,7 @@ private void updateMetadataForV2(Object object, EncryptionUtilsV2 encryptionUtil @SuppressLint("AndroidLintUseSparseArrays") // gson cannot handle sparse arrays easily, therefore use hashmap private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile parentFile) { RemoteOperationResult result = null; - File temporalFile = null; - File originalFile = new File(mOriginalStoragePath); - File expectedFile = null; - File encryptedTempFile = null; + E2EFiles e2eFiles = new E2EFiles(parentFile, null, new File(mOriginalStoragePath), null, null); FileLock fileLock = null; long size; @@ -636,7 +633,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); try { - result = checkConditions(originalFile); + result = checkConditions(e2eFiles.getOriginalFile()); if (result != null) { return result; @@ -668,17 +665,17 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare return collisionResult; } - mFile.setDecryptedRemotePath(parentFile.getDecryptedRemotePath() + originalFile.getName()); + mFile.setDecryptedRemotePath(parentFile.getDecryptedRemotePath() + e2eFiles.getOriginalFile().getName()); String expectedPath = FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mFile); - expectedFile = new File(expectedPath); + e2eFiles.setExpectedFile(new File(expectedPath)); - result = copyFile(originalFile, expectedPath); + result = copyFile(e2eFiles.getOriginalFile(), expectedPath); if (!result.isSuccess()) { return result; } - long lastModifiedTimestamp = originalFile.lastModified() / 1000; - Long creationTimestamp = FileUtil.getCreationTimestamp(originalFile); + long lastModifiedTimestamp = e2eFiles.getOriginalFile().lastModified() / 1000; + Long creationTimestamp = FileUtil.getCreationTimestamp(e2eFiles.getOriginalFile()); byte[] key = EncryptionUtils.generateKey(); byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength); @@ -687,7 +684,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare EncryptedFile encryptedFile = EncryptionUtils.encryptFile(user.getAccountName(), file, cipher); String encryptedFileName = getEncryptedFileName(object); - encryptedTempFile = encryptedFile.getEncryptedFile(); + e2eFiles.setEncryptedTempFile(encryptedFile.getEncryptedFile()); FileChannel channel = null; try { @@ -699,14 +696,14 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare String temporalPath = FileStorageUtils.getInternalTemporalPath(user.getAccountName(), mContext) + mFile.getRemotePath(); mFile.setStoragePath(temporalPath); - temporalFile = new File(temporalPath); + e2eFiles.setTemporalFile(new File(temporalPath)); Files.deleteIfExists(Paths.get(temporalPath)); - result = copy(originalFile, temporalFile); + result = copy(e2eFiles.getOriginalFile(), e2eFiles.getTemporalFile()); if (result.isSuccess()) { - if (temporalFile.length() == originalFile.length()) { - channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel(); + if (e2eFiles.getTemporalFile().length() == e2eFiles.getOriginalFile().length()) { + channel = new RandomAccessFile(e2eFiles.getTemporalFile().getAbsolutePath(), "rw").getChannel(); fileLock = channel.tryLock(); } else { result = new RemoteOperationResult(ResultCode.LOCK_FAILED); @@ -721,7 +718,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } updateSize(size); - setUploadFileRemoteOperationForE2E(token, encryptedTempFile, encryptedFileName, lastModifiedTimestamp, creationTimestamp, size); + setUploadFileRemoteOperationForE2E(token, e2eFiles.getEncryptedTempFile(), encryptedFileName, lastModifiedTimestamp, creationTimestamp, size); for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { mUploadOperation.addDataTransferProgressListener(mDataTransferListener); @@ -740,19 +737,9 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } if (result.isSuccess()) { - mFile.setDecryptedRemotePath(parentFile.getDecryptedRemotePath() + originalFile.getName()); - mFile.setRemotePath(parentFile.getRemotePath() + encryptedFileName); - - - if (object instanceof DecryptedFolderMetadataFileV1 metadata) { - updateMetadataForV1(metadata, key, iv, encryptedFile, - encryptedFileName, publicKey, parentFile, - arbitraryDataProvider, token, client, - metadataExists); - } else { - updateMetadataForV2(object, encryptionUtilsV2, encryptedFileName, key, - iv, encryptedFile, parentFile, token, client); - } + E2EData e2eData = new E2EData(key, iv, encryptedFile); + E2EClientData clientData = new E2EClientData(client, token, publicKey); + updateMetadataForE2E(object, e2eData, clientData, e2eFiles, encryptedFileName, arbitraryDataProvider, encryptionUtilsV2, metadataExists); } } catch (FileNotFoundException e) { Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore"); @@ -763,31 +750,52 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } catch (Exception e) { result = new RemoteOperationResult(e); } finally { - result = cleanupE2EUpload(fileLock, temporalFile, originalFile, result, object, parentFile, client, token, encryptedTempFile); + result = cleanupE2EUpload(fileLock, e2eFiles, result, object, client, token); } - completeE2EUpload(result, temporalFile, expectedFile, originalFile, client); + completeE2EUpload(result, e2eFiles, client); return result; } - private void completeE2EUpload(RemoteOperationResult result, File temporalFile, File expectedFile, File originalFile, OwnCloudClient client) { + private void updateMetadataForE2E(Object object, E2EData e2eData, E2EClientData clientData, E2EFiles e2eFiles, String encryptedFileName, ArbitraryDataProvider arbitraryDataProvider, EncryptionUtilsV2 encryptionUtilsV2, boolean metadataExists) + + throws InvalidAlgorithmParameterException, UploadException, NoSuchPaddingException, IllegalBlockSizeException, CertificateException, + NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + + mFile.setDecryptedRemotePath(e2eFiles.getParentFile().getDecryptedRemotePath() + e2eFiles.getOriginalFile().getName()); + mFile.setRemotePath(e2eFiles.getParentFile().getRemotePath() + encryptedFileName); + + + if (object instanceof DecryptedFolderMetadataFileV1 metadata) { + updateMetadataForV1(metadata, + e2eData, + clientData, + encryptedFileName, + e2eFiles.getParentFile(), + arbitraryDataProvider, + metadataExists); + } else { + updateMetadataForV2(object, encryptionUtilsV2, encryptedFileName, e2eData, clientData, e2eFiles.getParentFile()); + } + } + + private void completeE2EUpload(RemoteOperationResult result, E2EFiles e2eFiles, OwnCloudClient client) { if (result.isSuccess()) { - handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client); + handleSuccessfulUpload(e2eFiles.getTemporalFile(), e2eFiles.getExpectedFile(), e2eFiles.getOriginalFile(), client); } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); } - if (temporalFile != null && temporalFile.exists() && !temporalFile.delete()) { - Log_OC.e(TAG, "Could not delete temporal file " + temporalFile.getAbsolutePath()); + if (e2eFiles.getTemporalFile() != null && e2eFiles.getTemporalFile().exists() && !e2eFiles.getTemporalFile().delete()) { + Log_OC.e(TAG, "Could not delete temporal file " + e2eFiles.getTemporalFile().getAbsolutePath()); } } private RemoteOperationResult cleanupE2EUpload( - FileLock fileLock, File temporalFile, File originalFile, - RemoteOperationResult result, Object object, - OCFile parentFile, OwnCloudClient client, - String token, File encryptedTempFile) { + FileLock fileLock, E2EFiles e2eFiles, + RemoteOperationResult result, Object object, OwnCloudClient client, + String token) { mUploadStarted.set(false); sendRefreshFolderEventBroadcast(); @@ -800,8 +808,8 @@ private RemoteOperationResult cleanupE2EUpload( } } - if (temporalFile != null && !originalFile.equals(temporalFile)) { - temporalFile.delete(); + if (e2eFiles.getTemporalFile() != null && !e2eFiles.getOriginalFile().equals(e2eFiles.getTemporalFile())) { + e2eFiles.getTemporalFile().delete(); } if (result == null) { @@ -813,17 +821,17 @@ private RemoteOperationResult cleanupE2EUpload( // Unlock must be done otherwise folder stays locked and user can't upload any file RemoteOperationResult unlockFolderResult; if (object instanceof DecryptedFolderMetadataFileV1) { - unlockFolderResult = EncryptionUtils.unlockFolderV1(parentFile, client, token); + unlockFolderResult = EncryptionUtils.unlockFolderV1(e2eFiles.getParentFile(), client, token); } else { - unlockFolderResult = EncryptionUtils.unlockFolder(parentFile, client, token); + unlockFolderResult = EncryptionUtils.unlockFolder(e2eFiles.getParentFile(), client, token); } if (unlockFolderResult != null && !unlockFolderResult.isSuccess()) { result = unlockFolderResult; } - if (encryptedTempFile != null) { - boolean isTempEncryptedFileDeleted = encryptedTempFile.delete(); + if (e2eFiles.getEncryptedTempFile() != null) { + boolean isTempEncryptedFileDeleted = e2eFiles.getEncryptedTempFile().delete(); Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted); } else { Log_OC.e(TAG, "Encrypted temp file cannot be found"); diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt new file mode 100644 index 000000000000..604f890031a0 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt @@ -0,0 +1,12 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations.e2e + +import com.owncloud.android.lib.common.OwnCloudClient + +data class E2EClientData(val client: OwnCloudClient, val token: String, val publicKey: String) diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt new file mode 100644 index 000000000000..1aa9451d0b45 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt @@ -0,0 +1,12 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations.e2e + +import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFile + +data class E2EData(val key: ByteArray, val iv: ByteArray, val encryptedFile: EncryptedFile) diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt new file mode 100644 index 000000000000..a266aba0456d --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt @@ -0,0 +1,19 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations.e2e + +import com.owncloud.android.datamodel.OCFile +import java.io.File + +data class E2EFiles( + var parentFile: OCFile, + var temporalFile: File?, + var originalFile: File?, + var expectedFile: File?, + var encryptedTempFile: File? +) From db2a84b5fe75876edf2422a72a863dfbba177af3 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 12:34:43 +0200 Subject: [PATCH 06/16] Use different data classes Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 47 +++++++++++-------- .../android/operations/e2e/E2EData.kt | 7 ++- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 64d9bdcf0d80..34729c0f6df5 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -85,6 +85,7 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import java.security.spec.InvalidParameterSpecException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -547,7 +548,7 @@ private void setUploadFileRemoteOperationForE2E(String token, File encryptedTemp } } - private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, E2EData e2eData, E2EClientData clientData, String encryptedFileName, + private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, E2EData e2eData, E2EClientData clientData, OCFile parentFile, ArbitraryDataProvider arbitraryDataProvider, boolean metadataExists) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, @@ -562,7 +563,7 @@ private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, E2EData decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(e2eData.getIv())); decryptedFile.setAuthenticationTag(e2eData.getEncryptedFile().getAuthenticationTag()); - metadata.getFiles().put(encryptedFileName, decryptedFile); + metadata.getFiles().put(e2eData.getEncryptedFileName(), decryptedFile); EncryptedFolderMetadataFileV1 encryptedFolderMetadata = EncryptionUtils.encryptFolderMetadata(metadata, @@ -593,12 +594,11 @@ private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, E2EData } - private void updateMetadataForV2(Object object, EncryptionUtilsV2 encryptionUtilsV2, String encryptedFileName, - E2EData e2eData, E2EClientData clientData, OCFile parentFile) throws UploadException { + private void updateMetadataForV2(Object object, EncryptionUtilsV2 encryptionUtilsV2, E2EData e2eData, E2EClientData clientData, OCFile parentFile) throws UploadException { DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; encryptionUtilsV2.addFileToMetadata( - encryptedFileName, + e2eData.getEncryptedFileName(), mFile, e2eData.getIv(), e2eData.getEncryptedFile().getAuthenticationTag(), @@ -677,14 +677,8 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare long lastModifiedTimestamp = e2eFiles.getOriginalFile().lastModified() / 1000; Long creationTimestamp = FileUtil.getCreationTimestamp(e2eFiles.getOriginalFile()); - byte[] key = EncryptionUtils.generateKey(); - byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength); - Cipher cipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv); - File file = new File(mFile.getStoragePath()); - EncryptedFile encryptedFile = EncryptionUtils.encryptFile(user.getAccountName(), file, cipher); - String encryptedFileName = getEncryptedFileName(object); - - e2eFiles.setEncryptedTempFile(encryptedFile.getEncryptedFile()); + E2EData e2eData = getE2EData(object); + e2eFiles.setEncryptedTempFile(e2eData.getEncryptedFile().getEncryptedFile()); FileChannel channel = null; try { @@ -718,7 +712,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } updateSize(size); - setUploadFileRemoteOperationForE2E(token, e2eFiles.getEncryptedTempFile(), encryptedFileName, lastModifiedTimestamp, creationTimestamp, size); + setUploadFileRemoteOperationForE2E(token, e2eFiles.getEncryptedTempFile(), e2eData.getEncryptedFileName(), lastModifiedTimestamp, creationTimestamp, size); for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { mUploadOperation.addDataTransferProgressListener(mDataTransferListener); @@ -737,9 +731,8 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } if (result.isSuccess()) { - E2EData e2eData = new E2EData(key, iv, encryptedFile); E2EClientData clientData = new E2EClientData(client, token, publicKey); - updateMetadataForE2E(object, e2eData, clientData, e2eFiles, encryptedFileName, arbitraryDataProvider, encryptionUtilsV2, metadataExists); + updateMetadataForE2E(object, e2eData, clientData, e2eFiles, arbitraryDataProvider, encryptionUtilsV2, metadataExists); } } catch (FileNotFoundException e) { Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore"); @@ -758,25 +751,39 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare return result; } - private void updateMetadataForE2E(Object object, E2EData e2eData, E2EClientData clientData, E2EFiles e2eFiles, String encryptedFileName, ArbitraryDataProvider arbitraryDataProvider, EncryptionUtilsV2 encryptionUtilsV2, boolean metadataExists) + private E2EData getE2EData(Object object) throws InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidParameterSpecException, IOException { + byte[] key = EncryptionUtils.generateKey(); + byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength); + Cipher cipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv); + File file = new File(mFile.getStoragePath()); + EncryptedFile encryptedFile = EncryptionUtils.encryptFile(user.getAccountName(), file, cipher); + String encryptedFileName = getEncryptedFileName(object); + + if (key == null) { + throw new NullPointerException("key cannot be null"); + } + + return new E2EData(key, iv, encryptedFile, encryptedFileName); + } + + private void updateMetadataForE2E(Object object, E2EData e2eData, E2EClientData clientData, E2EFiles e2eFiles, ArbitraryDataProvider arbitraryDataProvider, EncryptionUtilsV2 encryptionUtilsV2, boolean metadataExists) throws InvalidAlgorithmParameterException, UploadException, NoSuchPaddingException, IllegalBlockSizeException, CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { mFile.setDecryptedRemotePath(e2eFiles.getParentFile().getDecryptedRemotePath() + e2eFiles.getOriginalFile().getName()); - mFile.setRemotePath(e2eFiles.getParentFile().getRemotePath() + encryptedFileName); + mFile.setRemotePath(e2eFiles.getParentFile().getRemotePath() + e2eData.getEncryptedFileName()); if (object instanceof DecryptedFolderMetadataFileV1 metadata) { updateMetadataForV1(metadata, e2eData, clientData, - encryptedFileName, e2eFiles.getParentFile(), arbitraryDataProvider, metadataExists); } else { - updateMetadataForV2(object, encryptionUtilsV2, encryptedFileName, e2eData, clientData, e2eFiles.getParentFile()); + updateMetadataForV2(object, encryptionUtilsV2, e2eData, clientData, e2eFiles.getParentFile()); } } diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt index 1aa9451d0b45..2063708d74af 100644 --- a/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt @@ -9,4 +9,9 @@ package com.owncloud.android.operations.e2e import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFile -data class E2EData(val key: ByteArray, val iv: ByteArray, val encryptedFile: EncryptedFile) +data class E2EData( + val key: ByteArray, + val iv: ByteArray, + val encryptedFile: EncryptedFile, + val encryptedFileName: String +) From 8725fa068b6cbe1d3449bdadc8346702f67123d0 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 13:27:58 +0200 Subject: [PATCH 07/16] Initialize E2EClientData earlier Signed-off-by: alperozturk --- .../com/owncloud/android/operations/UploadFileOperation.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 34729c0f6df5..60b481e59b23 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -657,6 +657,8 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare object = getDecryptedFolderMetadataV1(publicKey, object); } + E2EClientData clientData = new E2EClientData(client, token, publicKey); + List fileNames = checkNameCollision(object); RemoteOperationResult collisionResult = checkNameCollision(client, fileNames, parentFile.isEncrypted()); @@ -731,7 +733,6 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } if (result.isSuccess()) { - E2EClientData clientData = new E2EClientData(client, token, publicKey); updateMetadataForE2E(object, e2eData, clientData, e2eFiles, arbitraryDataProvider, encryptionUtilsV2, metadataExists); } } catch (FileNotFoundException e) { From 0ab2e1e34d048259e0f55d5a192b0a754ffbe4ca Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 13:56:07 +0200 Subject: [PATCH 08/16] Add initFileChannel Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 60b481e59b23..dced3dcad5b5 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -617,6 +617,36 @@ private void updateMetadataForV2(Object object, EncryptionUtilsV2 encryptionUtil getStorageManager()); } + private Pair initFileChannel(FileLock fileLock, E2EFiles e2eFiles) throws IOException { + FileChannel channel; + RemoteOperationResult result = null; + try { + channel = new RandomAccessFile(mFile.getStoragePath(), "rw").getChannel(); + fileLock = channel.tryLock(); + } catch (IOException e) { + // 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(user.getAccountName(), mContext) + + mFile.getRemotePath(); + mFile.setStoragePath(temporalPath); + e2eFiles.setTemporalFile(new File(temporalPath)); + + Files.deleteIfExists(Paths.get(temporalPath)); + result = copy(e2eFiles.getOriginalFile(), e2eFiles.getTemporalFile()); + + if (result.isSuccess()) { + if (e2eFiles.getTemporalFile().length() == e2eFiles.getOriginalFile().length()) { + channel = new RandomAccessFile(e2eFiles.getTemporalFile().getAbsolutePath(), "rw").getChannel(); + fileLock = channel.tryLock(); + } else { + result = new RemoteOperationResult(ResultCode.LOCK_FAILED); + } + } + } + + return new Pair<>(fileLock, result); + } + // TODO REFACTOR @SuppressLint("AndroidLintUseSparseArrays") // gson cannot handle sparse arrays easily, therefore use hashmap private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile parentFile) { @@ -682,30 +712,10 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare E2EData e2eData = getE2EData(object); e2eFiles.setEncryptedTempFile(e2eData.getEncryptedFile().getEncryptedFile()); - FileChannel channel = null; - try { - channel = new RandomAccessFile(mFile.getStoragePath(), "rw").getChannel(); - fileLock = channel.tryLock(); - } catch (FileNotFoundException e) { - // 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(user.getAccountName(), mContext) + - mFile.getRemotePath(); - mFile.setStoragePath(temporalPath); - e2eFiles.setTemporalFile(new File(temporalPath)); - - Files.deleteIfExists(Paths.get(temporalPath)); - result = copy(e2eFiles.getOriginalFile(), e2eFiles.getTemporalFile()); - if (result.isSuccess()) { - if (e2eFiles.getTemporalFile().length() == e2eFiles.getOriginalFile().length()) { - channel = new RandomAccessFile(e2eFiles.getTemporalFile().getAbsolutePath(), "rw").getChannel(); - fileLock = channel.tryLock(); - } else { - result = new RemoteOperationResult(ResultCode.LOCK_FAILED); - } - } - } + Pair channelResult = initFileChannel(fileLock, e2eFiles); + FileChannel channel = channelResult.first.channel(); + result = channelResult.second; try { size = channel.size(); From 7f8810e66f329ee25b42d644c03a94600a1f9eef Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 14:29:24 +0200 Subject: [PATCH 09/16] Filelock usage Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 153 +++++++++--------- 1 file changed, 78 insertions(+), 75 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index dced3dcad5b5..205ebfe43cef 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -101,6 +101,7 @@ import androidx.annotation.CheckResult; import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import kotlin.Triple; import static com.owncloud.android.ui.activity.FileDisplayActivity.REFRESH_FOLDER_EVENT_RECEIVER; @@ -548,77 +549,8 @@ private void setUploadFileRemoteOperationForE2E(String token, File encryptedTemp } } - private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, E2EData e2eData, E2EClientData clientData, - OCFile parentFile, ArbitraryDataProvider arbitraryDataProvider, boolean metadataExists) - - throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, - CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, UploadException { - - DecryptedFile decryptedFile = new DecryptedFile(); - Data data = new Data(); - data.setFilename(mFile.getDecryptedFileName()); - data.setMimetype(mFile.getMimeType()); - data.setKey(EncryptionUtils.encodeBytesToBase64String(e2eData.getKey())); - decryptedFile.setEncrypted(data); - decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(e2eData.getIv())); - decryptedFile.setAuthenticationTag(e2eData.getEncryptedFile().getAuthenticationTag()); - - metadata.getFiles().put(e2eData.getEncryptedFileName(), decryptedFile); - - EncryptedFolderMetadataFileV1 encryptedFolderMetadata = - EncryptionUtils.encryptFolderMetadata(metadata, - clientData.getPublicKey(), - parentFile.getLocalId(), - user, - arbitraryDataProvider - ); - - String serializedFolderMetadata; - - if (metadata.getMetadata().getMetadataKey() != null) { - serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata, true); - } else { - serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata); - } - - // upload metadata - EncryptionUtils.uploadMetadata(parentFile, - serializedFolderMetadata, - clientData.getToken(), - clientData.getClient(), - metadataExists, - E2EVersion.V1_2, - "", - arbitraryDataProvider, - user); - } - - - private void updateMetadataForV2(Object object, EncryptionUtilsV2 encryptionUtilsV2, E2EData e2eData, E2EClientData clientData, OCFile parentFile) throws UploadException { - - DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; - encryptionUtilsV2.addFileToMetadata( - e2eData.getEncryptedFileName(), - mFile, - e2eData.getIv(), - e2eData.getEncryptedFile().getAuthenticationTag(), - e2eData.getKey(), - metadata, - getStorageManager()); - - // upload metadata - encryptionUtilsV2.serializeAndUploadMetadata(parentFile, - metadata, - clientData.getToken(), - clientData.getClient(), - true, - mContext, - user, - getStorageManager()); - } - - private Pair initFileChannel(FileLock fileLock, E2EFiles e2eFiles) throws IOException { - FileChannel channel; + private Triple initFileChannel(FileLock fileLock, E2EFiles e2eFiles) throws IOException { + FileChannel channel = null; RemoteOperationResult result = null; try { channel = new RandomAccessFile(mFile.getStoragePath(), "rw").getChannel(); @@ -644,7 +576,7 @@ private Pair initFileChannel(FileLock fileLock, } } - return new Pair<>(fileLock, result); + return new Triple<>(fileLock, result, channel); } // TODO REFACTOR @@ -713,9 +645,11 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare e2eFiles.setEncryptedTempFile(e2eData.getEncryptedFile().getEncryptedFile()); - Pair channelResult = initFileChannel(fileLock, e2eFiles); - FileChannel channel = channelResult.first.channel(); - result = channelResult.second; + Triple channelResult = initFileChannel(fileLock, e2eFiles); + fileLock = channelResult.getFirst(); + result = channelResult.getSecond(); + FileChannel channel = channelResult.getThird(); + try { size = channel.size(); @@ -798,6 +732,75 @@ private void updateMetadataForE2E(Object object, E2EData e2eData, E2EClientData } } + private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, E2EData e2eData, E2EClientData clientData, + OCFile parentFile, ArbitraryDataProvider arbitraryDataProvider, boolean metadataExists) + + throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, + CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, UploadException { + + DecryptedFile decryptedFile = new DecryptedFile(); + Data data = new Data(); + data.setFilename(mFile.getDecryptedFileName()); + data.setMimetype(mFile.getMimeType()); + data.setKey(EncryptionUtils.encodeBytesToBase64String(e2eData.getKey())); + decryptedFile.setEncrypted(data); + decryptedFile.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(e2eData.getIv())); + decryptedFile.setAuthenticationTag(e2eData.getEncryptedFile().getAuthenticationTag()); + + metadata.getFiles().put(e2eData.getEncryptedFileName(), decryptedFile); + + EncryptedFolderMetadataFileV1 encryptedFolderMetadata = + EncryptionUtils.encryptFolderMetadata(metadata, + clientData.getPublicKey(), + parentFile.getLocalId(), + user, + arbitraryDataProvider + ); + + String serializedFolderMetadata; + + if (metadata.getMetadata().getMetadataKey() != null) { + serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata, true); + } else { + serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata); + } + + // upload metadata + EncryptionUtils.uploadMetadata(parentFile, + serializedFolderMetadata, + clientData.getToken(), + clientData.getClient(), + metadataExists, + E2EVersion.V1_2, + "", + arbitraryDataProvider, + user); + } + + + private void updateMetadataForV2(Object object, EncryptionUtilsV2 encryptionUtilsV2, E2EData e2eData, E2EClientData clientData, OCFile parentFile) throws UploadException { + + DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; + encryptionUtilsV2.addFileToMetadata( + e2eData.getEncryptedFileName(), + mFile, + e2eData.getIv(), + e2eData.getEncryptedFile().getAuthenticationTag(), + e2eData.getKey(), + metadata, + getStorageManager()); + + // upload metadata + encryptionUtilsV2.serializeAndUploadMetadata(parentFile, + metadata, + clientData.getToken(), + clientData.getClient(), + true, + mContext, + user, + getStorageManager()); + } + private void completeE2EUpload(RemoteOperationResult result, E2EFiles e2eFiles, OwnCloudClient client) { if (result.isSuccess()) { handleSuccessfulUpload(e2eFiles.getTemporalFile(), e2eFiles.getExpectedFile(), e2eFiles.getOriginalFile(), client); From 323fa0827bce12ba4678107d57dbb49661839a03 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 14:34:23 +0200 Subject: [PATCH 10/16] extract performE2EUpload Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 205ebfe43cef..eaea8c46ac80 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -644,37 +644,16 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare E2EData e2eData = getE2EData(object); e2eFiles.setEncryptedTempFile(e2eData.getEncryptedFile().getEncryptedFile()); - Triple channelResult = initFileChannel(fileLock, e2eFiles); fileLock = channelResult.getFirst(); result = channelResult.getSecond(); FileChannel channel = channelResult.getThird(); - - try { - size = channel.size(); - } catch (IOException e1) { - size = new File(mFile.getStoragePath()).length(); - } - + size = initSize(channel); updateSize(size); setUploadFileRemoteOperationForE2E(token, e2eFiles.getEncryptedTempFile(), e2eData.getEncryptedFileName(), lastModifiedTimestamp, creationTimestamp, size); - for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { - mUploadOperation.addDataTransferProgressListener(mDataTransferListener); - } - - if (mCancellationRequested.get()) { - throw new OperationCancelledException(); - } - - result = mUploadOperation.execute(client); - - /// move local temporal file or original file to its corresponding - // location in the Nextcloud local folder - if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { - result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); - } + result = performE2EUpload(clientData); if (result.isSuccess()) { updateMetadataForE2E(object, e2eData, clientData, e2eFiles, arbitraryDataProvider, encryptionUtilsV2, metadataExists); @@ -696,6 +675,35 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare return result; } + private long initSize(FileChannel channel) { + try { + return channel.size(); + } catch (IOException e1) { + return new File(mFile.getStoragePath()).length(); + } + } + + private RemoteOperationResult performE2EUpload(E2EClientData data) throws OperationCancelledException { + RemoteOperationResult result; + for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { + mUploadOperation.addDataTransferProgressListener(mDataTransferListener); + } + + if (mCancellationRequested.get()) { + throw new OperationCancelledException(); + } + + result = mUploadOperation.execute(data.getClient()); + + /// move local temporal file or original file to its corresponding + // location in the Nextcloud local folder + if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { + result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); + } + + return result; + } + private E2EData getE2EData(Object object) throws InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidParameterSpecException, IOException { byte[] key = EncryptionUtils.generateKey(); byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength); From 21968c1411df221aa6936fd35368f76ee6acf26f Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 14:38:38 +0200 Subject: [PATCH 11/16] Use region Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 193 +++++++++--------- 1 file changed, 97 insertions(+), 96 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index eaea8c46ac80..3bbfb8f5809e 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -448,6 +448,102 @@ protected RemoteOperationResult run(OwnCloudClient client) { } } + // region E2E Upload + @SuppressLint("AndroidLintUseSparseArrays") // gson cannot handle sparse arrays easily, therefore use hashmap + private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile parentFile) { + RemoteOperationResult result = null; + E2EFiles e2eFiles = new E2EFiles(parentFile, null, new File(mOriginalStoragePath), null, null); + FileLock fileLock = null; + long size; + + boolean metadataExists = false; + String token = null; + Object object = null; + + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(getContext()); + String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); + + try { + result = checkConditions(e2eFiles.getOriginalFile()); + + if (result != null) { + return result; + } + + long counter = getE2ECounter(parentFile); + token = getToken(client, parentFile, counter); + + // Update metadata + EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2(); + object = EncryptionUtils.downloadFolderMetadata(parentFile, client, mContext, user); + if (object instanceof DecryptedFolderMetadataFileV1 decrypted && decrypted.getMetadata() != null) { + metadataExists = true; + } + + if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) { + if (object == null) { + return new RemoteOperationResult(new IllegalStateException("Metadata does not exist")); + } + } else { + object = getDecryptedFolderMetadataV1(publicKey, object); + } + + E2EClientData clientData = new E2EClientData(client, token, publicKey); + + List fileNames = checkNameCollision(object); + + RemoteOperationResult collisionResult = checkNameCollision(client, fileNames, parentFile.isEncrypted()); + if (collisionResult != null) { + result = collisionResult; + return collisionResult; + } + + mFile.setDecryptedRemotePath(parentFile.getDecryptedRemotePath() + e2eFiles.getOriginalFile().getName()); + String expectedPath = FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mFile); + e2eFiles.setExpectedFile(new File(expectedPath)); + + result = copyFile(e2eFiles.getOriginalFile(), expectedPath); + if (!result.isSuccess()) { + return result; + } + + long lastModifiedTimestamp = e2eFiles.getOriginalFile().lastModified() / 1000; + Long creationTimestamp = FileUtil.getCreationTimestamp(e2eFiles.getOriginalFile()); + + E2EData e2eData = getE2EData(object); + e2eFiles.setEncryptedTempFile(e2eData.getEncryptedFile().getEncryptedFile()); + + Triple channelResult = initFileChannel(fileLock, e2eFiles); + fileLock = channelResult.getFirst(); + result = channelResult.getSecond(); + FileChannel channel = channelResult.getThird(); + + size = initSize(channel); + updateSize(size); + setUploadFileRemoteOperationForE2E(token, e2eFiles.getEncryptedTempFile(), e2eData.getEncryptedFileName(), lastModifiedTimestamp, creationTimestamp, size); + + result = performE2EUpload(clientData); + + if (result.isSuccess()) { + updateMetadataForE2E(object, e2eData, clientData, e2eFiles, arbitraryDataProvider, encryptionUtilsV2, metadataExists); + } + } catch (FileNotFoundException e) { + Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore"); + result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); + } catch (OverlappingFileLockException e) { + Log_OC.d(TAG, "Overlapping file lock exception"); + result = new RemoteOperationResult(ResultCode.LOCK_FAILED); + } catch (Exception e) { + result = new RemoteOperationResult(e); + } finally { + result = cleanupE2EUpload(fileLock, e2eFiles, result, object, client, token); + } + + completeE2EUpload(result, e2eFiles, client); + + return result; + } + private String getToken(OwnCloudClient client, OCFile parentFile, long counter) throws UploadException { String token; if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) { @@ -579,102 +675,6 @@ private Triple initFileChannel(Fil return new Triple<>(fileLock, result, channel); } - // TODO REFACTOR - @SuppressLint("AndroidLintUseSparseArrays") // gson cannot handle sparse arrays easily, therefore use hashmap - private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile parentFile) { - RemoteOperationResult result = null; - E2EFiles e2eFiles = new E2EFiles(parentFile, null, new File(mOriginalStoragePath), null, null); - FileLock fileLock = null; - long size; - - boolean metadataExists = false; - String token = null; - Object object = null; - - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(getContext()); - String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); - - try { - result = checkConditions(e2eFiles.getOriginalFile()); - - if (result != null) { - return result; - } - - long counter = getE2ECounter(parentFile); - token = getToken(client, parentFile, counter); - - // Update metadata - EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2(); - object = EncryptionUtils.downloadFolderMetadata(parentFile, client, mContext, user); - if (object instanceof DecryptedFolderMetadataFileV1 decrypted && decrypted.getMetadata() != null) { - metadataExists = true; - } - - if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) { - if (object == null) { - return new RemoteOperationResult(new IllegalStateException("Metadata does not exist")); - } - } else { - object = getDecryptedFolderMetadataV1(publicKey, object); - } - - E2EClientData clientData = new E2EClientData(client, token, publicKey); - - List fileNames = checkNameCollision(object); - - RemoteOperationResult collisionResult = checkNameCollision(client, fileNames, parentFile.isEncrypted()); - if (collisionResult != null) { - result = collisionResult; - return collisionResult; - } - - mFile.setDecryptedRemotePath(parentFile.getDecryptedRemotePath() + e2eFiles.getOriginalFile().getName()); - String expectedPath = FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mFile); - e2eFiles.setExpectedFile(new File(expectedPath)); - - result = copyFile(e2eFiles.getOriginalFile(), expectedPath); - if (!result.isSuccess()) { - return result; - } - - long lastModifiedTimestamp = e2eFiles.getOriginalFile().lastModified() / 1000; - Long creationTimestamp = FileUtil.getCreationTimestamp(e2eFiles.getOriginalFile()); - - E2EData e2eData = getE2EData(object); - e2eFiles.setEncryptedTempFile(e2eData.getEncryptedFile().getEncryptedFile()); - - Triple channelResult = initFileChannel(fileLock, e2eFiles); - fileLock = channelResult.getFirst(); - result = channelResult.getSecond(); - FileChannel channel = channelResult.getThird(); - - size = initSize(channel); - updateSize(size); - setUploadFileRemoteOperationForE2E(token, e2eFiles.getEncryptedTempFile(), e2eData.getEncryptedFileName(), lastModifiedTimestamp, creationTimestamp, size); - - result = performE2EUpload(clientData); - - if (result.isSuccess()) { - updateMetadataForE2E(object, e2eData, clientData, e2eFiles, arbitraryDataProvider, encryptionUtilsV2, metadataExists); - } - } catch (FileNotFoundException e) { - Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore"); - result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); - } catch (OverlappingFileLockException e) { - Log_OC.d(TAG, "Overlapping file lock exception"); - result = new RemoteOperationResult(ResultCode.LOCK_FAILED); - } catch (Exception e) { - result = new RemoteOperationResult(e); - } finally { - result = cleanupE2EUpload(fileLock, e2eFiles, result, object, client, token); - } - - completeE2EUpload(result, e2eFiles, client); - - return result; - } - private long initSize(FileChannel channel) { try { return channel.size(); @@ -868,6 +868,7 @@ private RemoteOperationResult cleanupE2EUpload( return result; } + // endregion private void sendRefreshFolderEventBroadcast() { Intent intent = new Intent(REFRESH_FOLDER_EVENT_RECEIVER); From 34aa3b6cc79e5a431abbec4a95691a07df8eb787 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 16:18:39 +0200 Subject: [PATCH 12/16] Check nullable fields Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 67 ++++++++++++------- .../android/operations/e2e/E2EFiles.kt | 32 ++++++++- 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 3bbfb8f5809e..f090d20abcf8 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -15,7 +15,6 @@ import android.content.Intent; import android.net.Uri; import android.text.TextUtils; -import android.util.Pair; import com.nextcloud.client.account.User; import com.nextcloud.client.device.BatteryStatus; @@ -509,11 +508,17 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare long lastModifiedTimestamp = e2eFiles.getOriginalFile().lastModified() / 1000; Long creationTimestamp = FileUtil.getCreationTimestamp(e2eFiles.getOriginalFile()); + if (creationTimestamp == null) { + throw new NullPointerException("creationTimestamp cannot be null"); + } E2EData e2eData = getE2EData(object); e2eFiles.setEncryptedTempFile(e2eData.getEncryptedFile().getEncryptedFile()); + if (e2eFiles.getEncryptedTempFile() == null) { + throw new NullPointerException("encryptedTempFile cannot be null"); + } - Triple channelResult = initFileChannel(fileLock, e2eFiles); + Triple channelResult = initFileChannel(result, fileLock, e2eFiles); fileLock = channelResult.getFirst(); result = channelResult.getSecond(); FileChannel channel = channelResult.getThird(); @@ -522,7 +527,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare updateSize(size); setUploadFileRemoteOperationForE2E(token, e2eFiles.getEncryptedTempFile(), e2eData.getEncryptedFileName(), lastModifiedTimestamp, creationTimestamp, size); - result = performE2EUpload(clientData); + result = performE2EUpload(result, clientData); if (result.isSuccess()) { updateMetadataForE2E(object, e2eData, clientData, e2eFiles, arbitraryDataProvider, encryptionUtilsV2, metadataExists); @@ -568,6 +573,7 @@ private long getE2ECounter(OCFile parentFile) { private DecryptedFolderMetadataFileV1 getDecryptedFolderMetadataV1(String publicKey, Object object) throws NoSuchPaddingException, IllegalBlockSizeException, CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + DecryptedFolderMetadataFileV1 metadata = new DecryptedFolderMetadataFileV1(); metadata.setMetadata(new DecryptedMetadata()); metadata.getMetadata().setVersion(1.2); @@ -645,11 +651,16 @@ private void setUploadFileRemoteOperationForE2E(String token, File encryptedTemp } } - private Triple initFileChannel(FileLock fileLock, E2EFiles e2eFiles) throws IOException { + private Triple initFileChannel(RemoteOperationResult result, FileLock fileLock, E2EFiles e2eFiles) throws IOException { FileChannel channel = null; - RemoteOperationResult result = null; + try { - channel = new RandomAccessFile(mFile.getStoragePath(), "rw").getChannel(); + channel = getChannelFromFile(mFile.getStoragePath()); + + if (channel == null) { + throw new NullPointerException("channel cannot be null"); + } + fileLock = channel.tryLock(); } catch (IOException e) { // this basically means that the file is on SD card @@ -659,12 +670,21 @@ private Triple initFileChannel(Fil mFile.setStoragePath(temporalPath); e2eFiles.setTemporalFile(new File(temporalPath)); + if (e2eFiles.getTemporalFile() == null) { + throw new NullPointerException("Original file cannot be null"); + } + Files.deleteIfExists(Paths.get(temporalPath)); result = copy(e2eFiles.getOriginalFile(), e2eFiles.getTemporalFile()); if (result.isSuccess()) { if (e2eFiles.getTemporalFile().length() == e2eFiles.getOriginalFile().length()) { - channel = new RandomAccessFile(e2eFiles.getTemporalFile().getAbsolutePath(), "rw").getChannel(); + channel = getChannelFromFile(e2eFiles.getTemporalFile().getAbsolutePath()); + + if (channel == null) { + throw new NullPointerException("channel cannot be null"); + } + fileLock = channel.tryLock(); } else { result = new RemoteOperationResult(ResultCode.LOCK_FAILED); @@ -675,6 +695,15 @@ private Triple initFileChannel(Fil return new Triple<>(fileLock, result, channel); } + private FileChannel getChannelFromFile(String path) { + try (RandomAccessFile randomAccessFile = new RandomAccessFile(path, "rw")) { + return randomAccessFile.getChannel(); + } catch (IOException e) { + Log_OC.d(TAG, "Error caught at getChannelFromFile: " + e); + return null; + } + } + private long initSize(FileChannel channel) { try { return channel.size(); @@ -683,8 +712,7 @@ private long initSize(FileChannel channel) { } } - private RemoteOperationResult performE2EUpload(E2EClientData data) throws OperationCancelledException { - RemoteOperationResult result; + private RemoteOperationResult performE2EUpload( RemoteOperationResult result, E2EClientData data) throws OperationCancelledException { for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { mUploadOperation.addDataTransferProgressListener(mDataTransferListener); } @@ -816,16 +844,10 @@ private void completeE2EUpload(RemoteOperationResult result, E2EFiles e2eFiles, getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); } - if (e2eFiles.getTemporalFile() != null && e2eFiles.getTemporalFile().exists() && !e2eFiles.getTemporalFile().delete()) { - Log_OC.e(TAG, "Could not delete temporal file " + e2eFiles.getTemporalFile().getAbsolutePath()); - } + e2eFiles.deleteTemporalFile(); } - private RemoteOperationResult cleanupE2EUpload( - FileLock fileLock, E2EFiles e2eFiles, - RemoteOperationResult result, Object object, OwnCloudClient client, - String token) { - + private RemoteOperationResult cleanupE2EUpload(FileLock fileLock, E2EFiles e2eFiles, RemoteOperationResult result, Object object, OwnCloudClient client, String token) { mUploadStarted.set(false); sendRefreshFolderEventBroadcast(); @@ -837,9 +859,7 @@ private RemoteOperationResult cleanupE2EUpload( } } - if (e2eFiles.getTemporalFile() != null && !e2eFiles.getOriginalFile().equals(e2eFiles.getTemporalFile())) { - e2eFiles.getTemporalFile().delete(); - } + e2eFiles.deleteTemporalFileWithOriginalFileComparison(); if (result == null) { result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); @@ -859,12 +879,7 @@ private RemoteOperationResult cleanupE2EUpload( result = unlockFolderResult; } - if (e2eFiles.getEncryptedTempFile() != null) { - boolean isTempEncryptedFileDeleted = e2eFiles.getEncryptedTempFile().delete(); - Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted); - } else { - Log_OC.e(TAG, "Encrypted temp file cannot be found"); - } + e2eFiles.deleteEncryptedTempFile(); return result; } diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt index a266aba0456d..e7c320c462d1 100644 --- a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt @@ -8,12 +8,40 @@ package com.owncloud.android.operations.e2e import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.operations.UploadFileOperation import java.io.File data class E2EFiles( var parentFile: OCFile, var temporalFile: File?, - var originalFile: File?, + var originalFile: File, var expectedFile: File?, var encryptedTempFile: File? -) +) { + private val tag = "E2EFiles" + + fun deleteTemporalFile() { + if (temporalFile?.exists() == true && temporalFile?.delete() == false) { + Log_OC.e(tag, "Could not delete temporal file " + temporalFile?.absolutePath) + } + } + + fun deleteTemporalFileWithOriginalFileComparison() { + if (originalFile == temporalFile) { + return + } + + val isTemporalFileDeleted = temporalFile?.delete() + Log_OC.d(tag, "isTemporalFileDeleted: $isTemporalFileDeleted") + } + + fun deleteEncryptedTempFile() { + if (encryptedTempFile != null) { + val isTempEncryptedFileDeleted = encryptedTempFile?.delete() + Log_OC.e(tag, "isTempEncryptedFileDeleted: $isTempEncryptedFileDeleted") + } else { + Log_OC.e(tag, "Encrypted temp file cannot be found") + } + } +} From 25a65473d41d65651c7f7defbf3faacf6c99aa49 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 16:32:59 +0200 Subject: [PATCH 13/16] Check nullable fields Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index f090d20abcf8..9509db316fad 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -98,6 +98,7 @@ import javax.crypto.NoSuchPaddingException; import androidx.annotation.CheckResult; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import kotlin.Triple; @@ -622,8 +623,12 @@ private String getEncryptedFileName(Object object) { return encryptedFileName; } - private void setUploadFileRemoteOperationForE2E(String token, File encryptedTempFile, String encryptedFileName, - long lastModifiedTimestamp, long creationTimestamp, long size) { + private void setUploadFileRemoteOperationForE2E(String token, + File encryptedTempFile, + String encryptedFileName, + long lastModifiedTimestamp, + long creationTimestamp, + long size) { if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { boolean onWifiConnection = connectivityService.getConnectivity().isWifi(); @@ -654,15 +659,12 @@ private void setUploadFileRemoteOperationForE2E(String token, File encryptedTemp private Triple initFileChannel(RemoteOperationResult result, FileLock fileLock, E2EFiles e2eFiles) throws IOException { FileChannel channel = null; - try { - channel = getChannelFromFile(mFile.getStoragePath()); - - if (channel == null) { - throw new NullPointerException("channel cannot be null"); - } - + try (RandomAccessFile randomAccessFile = new RandomAccessFile(mFile.getStoragePath(), "rw")) { + channel = randomAccessFile.getChannel(); fileLock = channel.tryLock(); - } catch (IOException e) { + } catch (IOException ioException) { + Log_OC.d(TAG, "Error caught at getChannelFromFile: " + ioException); + // 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(user.getAccountName(), mContext) + @@ -679,13 +681,12 @@ private Triple initFileChannel(Rem if (result.isSuccess()) { if (e2eFiles.getTemporalFile().length() == e2eFiles.getOriginalFile().length()) { - channel = getChannelFromFile(e2eFiles.getTemporalFile().getAbsolutePath()); - - if (channel == null) { - throw new NullPointerException("channel cannot be null"); + try (RandomAccessFile randomAccessFile = new RandomAccessFile(e2eFiles.getTemporalFile().getAbsolutePath(), "rw")) { + channel = randomAccessFile.getChannel(); + fileLock = channel.tryLock(); + } catch (IOException e) { + Log_OC.d(TAG, "Error caught at getChannelFromFile: " + e); } - - fileLock = channel.tryLock(); } else { result = new RemoteOperationResult(ResultCode.LOCK_FAILED); } @@ -695,15 +696,6 @@ private Triple initFileChannel(Rem return new Triple<>(fileLock, result, channel); } - private FileChannel getChannelFromFile(String path) { - try (RandomAccessFile randomAccessFile = new RandomAccessFile(path, "rw")) { - return randomAccessFile.getChannel(); - } catch (IOException e) { - Log_OC.d(TAG, "Error caught at getChannelFromFile: " + e); - return null; - } - } - private long initSize(FileChannel channel) { try { return channel.size(); From bb36e3211e9bdc444daa8163a55c9609ca7d34eb Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 25 Apr 2024 16:37:42 +0200 Subject: [PATCH 14/16] Fix kotlin spotless Signed-off-by: alperozturk --- .../android/operations/UploadFileOperation.java | 12 +++++++----- .../com/owncloud/android/operations/e2e/E2EFiles.kt | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 9509db316fad..88b7c4b443ca 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -755,8 +755,12 @@ private void updateMetadataForE2E(Object object, E2EData e2eData, E2EClientData e2eFiles.getParentFile(), arbitraryDataProvider, metadataExists); - } else { - updateMetadataForV2(object, encryptionUtilsV2, e2eData, clientData, e2eFiles.getParentFile()); + } else if (object instanceof DecryptedFolderMetadataFile metadata) { + updateMetadataForV2(metadata, + encryptionUtilsV2, + e2eData, + clientData, + e2eFiles.getParentFile()); } } @@ -806,9 +810,7 @@ private void updateMetadataForV1(DecryptedFolderMetadataFileV1 metadata, E2EData } - private void updateMetadataForV2(Object object, EncryptionUtilsV2 encryptionUtilsV2, E2EData e2eData, E2EClientData clientData, OCFile parentFile) throws UploadException { - - DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; + private void updateMetadataForV2(DecryptedFolderMetadataFile metadata, EncryptionUtilsV2 encryptionUtilsV2, E2EData e2eData, E2EClientData clientData, OCFile parentFile) throws UploadException { encryptionUtilsV2.addFileToMetadata( e2eData.getEncryptedFileName(), mFile, diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt index e7c320c462d1..b201775b2d3f 100644 --- a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt @@ -9,7 +9,6 @@ package com.owncloud.android.operations.e2e import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.operations.UploadFileOperation import java.io.File data class E2EFiles( From 82af5b4787a0142b83848be328adce89393a093b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 26 Apr 2024 08:17:56 +0200 Subject: [PATCH 15/16] Rename functions Signed-off-by: alperozturk --- .../operations/UploadFileOperation.java | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 88b7c4b443ca..f2fc4223fa22 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -89,6 +89,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -98,7 +99,6 @@ import javax.crypto.NoSuchPaddingException; import androidx.annotation.CheckResult; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import kotlin.Triple; @@ -471,7 +471,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } long counter = getE2ECounter(parentFile); - token = getToken(client, parentFile, counter); + token = getFolderUnlockTokenOrLockFolder(client, parentFile, counter); // Update metadata EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2(); @@ -480,7 +480,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare metadataExists = true; } - if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) { + if (isEndToEndVersionAtLeastV2()) { if (object == null) { return new RemoteOperationResult(new IllegalStateException("Metadata does not exist")); } @@ -490,7 +490,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare E2EClientData clientData = new E2EClientData(client, token, publicKey); - List fileNames = checkNameCollision(object); + List fileNames = getCollidedFileNames(object); RemoteOperationResult collisionResult = checkNameCollision(client, fileNames, parentFile.isEncrypted()); if (collisionResult != null) { @@ -524,11 +524,11 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare result = channelResult.getSecond(); FileChannel channel = channelResult.getThird(); - size = initSize(channel); + size = getChannelSize(channel); updateSize(size); - setUploadFileRemoteOperationForE2E(token, e2eFiles.getEncryptedTempFile(), e2eData.getEncryptedFileName(), lastModifiedTimestamp, creationTimestamp, size); + setUploadOperationForE2E(token, e2eFiles.getEncryptedTempFile(), e2eData.getEncryptedFileName(), lastModifiedTimestamp, creationTimestamp, size); - result = performE2EUpload(result, clientData); + result = performE2EUpload(clientData); if (result.isSuccess()) { updateMetadataForE2E(object, e2eData, clientData, e2eFiles, arbitraryDataProvider, encryptionUtilsV2, metadataExists); @@ -550,28 +550,36 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare return result; } - private String getToken(OwnCloudClient client, OCFile parentFile, long counter) throws UploadException { - String token; - if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) { - token = mFolderUnlockToken; - } else { - token = EncryptionUtils.lockFolder(parentFile, client, counter); - mUpload.setFolderUnlockToken(token); - uploadsStorageManager.updateUpload(mUpload); - } + private boolean isEndToEndVersionAtLeastV2() { + return getE2EVersion().compareTo(E2EVersion.V2_0) >= 0; + } - return token; + private E2EVersion getE2EVersion() { + return CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion(); } private long getE2ECounter(OCFile parentFile) { long counter = -1; - if (CapabilityUtils.getCapability(mContext).getEndToEndEncryptionApiVersion().compareTo(E2EVersion.V2_0) >= 0) { + + if (isEndToEndVersionAtLeastV2()) { counter = parentFile.getE2eCounter() + 1; } return counter; } + private String getFolderUnlockTokenOrLockFolder(OwnCloudClient client, OCFile parentFile, long counter) throws UploadException { + if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) { + return mFolderUnlockToken; + } + + String token = EncryptionUtils.lockFolder(parentFile, client, counter); + mUpload.setFolderUnlockToken(token); + uploadsStorageManager.updateUpload(mUpload); + + return token; + } + private DecryptedFolderMetadataFileV1 getDecryptedFolderMetadataV1(String publicKey, Object object) throws NoSuchPaddingException, IllegalBlockSizeException, CertificateException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { @@ -590,21 +598,21 @@ private DecryptedFolderMetadataFileV1 getDecryptedFolderMetadataV1(String public return metadata; } - private List checkNameCollision(Object object) { - List fileNames = new ArrayList<>(); + private List getCollidedFileNames(Object object) { + List result = new ArrayList<>(); if (object instanceof DecryptedFolderMetadataFileV1 metadata) { for (DecryptedFile file : metadata.getFiles().values()) { - fileNames.add(file.getEncrypted().getFilename()); + result.add(file.getEncrypted().getFilename()); } - } else { - for (com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile file : - ((DecryptedFolderMetadataFile) object).getMetadata().getFiles().values()) { - fileNames.add(file.getFilename()); + } else if (object instanceof DecryptedFolderMetadataFile metadataFile) { + Map files = metadataFile.getMetadata().getFiles(); + for (com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile file : files.values()) { + result.add(file.getFilename()); } } - return fileNames; + return result; } private String getEncryptedFileName(Object object) { @@ -623,12 +631,12 @@ private String getEncryptedFileName(Object object) { return encryptedFileName; } - private void setUploadFileRemoteOperationForE2E(String token, - File encryptedTempFile, - String encryptedFileName, - long lastModifiedTimestamp, - long creationTimestamp, - long size) { + private void setUploadOperationForE2E(String token, + File encryptedTempFile, + String encryptedFileName, + long lastModifiedTimestamp, + long creationTimestamp, + long size) { if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { boolean onWifiConnection = connectivityService.getConnectivity().isWifi(); @@ -696,7 +704,7 @@ private Triple initFileChannel(Rem return new Triple<>(fileLock, result, channel); } - private long initSize(FileChannel channel) { + private long getChannelSize(FileChannel channel) { try { return channel.size(); } catch (IOException e1) { @@ -704,7 +712,7 @@ private long initSize(FileChannel channel) { } } - private RemoteOperationResult performE2EUpload( RemoteOperationResult result, E2EClientData data) throws OperationCancelledException { + private RemoteOperationResult performE2EUpload(E2EClientData data) throws OperationCancelledException { for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { mUploadOperation.addDataTransferProgressListener(mDataTransferListener); } @@ -713,7 +721,7 @@ private RemoteOperationResult performE2EUpload( RemoteOperationResult result, E2 throw new OperationCancelledException(); } - result = mUploadOperation.execute(data.getClient()); + RemoteOperationResult result = mUploadOperation.execute(data.getClient()); /// move local temporal file or original file to its corresponding // location in the Nextcloud local folder From 956b6755a79206e4cf8ebc409f54e90e8b541b96 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Fri, 3 May 2024 02:42:21 +0000 Subject: [PATCH 16/16] Fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-tr/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 91c82f0b948e..e0c3ef5096db 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -88,6 +88,8 @@ Sunucu adı bulunamadı %1$s birden çok hesabı desteklemiyor Bağlantı kurulamadı + Oturum açmaktan vazgeç + Lütfen oturum açma işlemini tarayıcınızdan tamamlayın salt okunur olduğundan özgün klasörde kaldı Yalnızca kullanıma göre ücretlendirilmeyen kablosuz ağ üzerinden yüklensin /OtomatikYükleme