Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@
import com.owncloud.android.operations.upload.RemoteFileExistence;
import com.owncloud.android.operations.upload.UploadFileException;
import com.owncloud.android.operations.upload.UploadFileOperationExtensionsKt;
import com.owncloud.android.operations.upload.UploadFileResolver;
import com.owncloud.android.utils.EncryptionUtils;
import com.owncloud.android.utils.EncryptionUtilsV2;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.FileUtil;
import com.owncloud.android.utils.MimeType;
import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.UriUtils;
import com.owncloud.android.utils.theme.CapabilityUtils;
Expand Down Expand Up @@ -95,11 +95,9 @@
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;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

Expand Down Expand Up @@ -175,29 +173,10 @@ public class UploadFileOperation extends SyncOperation {
private volatile boolean encryptedAncestor;
private OCFile duplicatedEncryptedFile;
private AtomicBoolean missingPermissionThrown = new AtomicBoolean(false);
private final UploadFileResolver uploadFileResolver;

public static OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType) {
OCFile newFile = new OCFile(remotePath);
newFile.setStoragePath(localPath);
newFile.setLastSyncDateForProperties(0);
newFile.setLastSyncDateForData(0);

// size
if (!TextUtils.isEmpty(localPath)) {
File localFile = new File(localPath);
newFile.setFileLength(localFile.length());
newFile.setLastSyncDateForData(localFile.lastModified());
} // don't worry about not assigning size, the problems with localPath
// are checked when the UploadFileOperation instance is created

// MIME type
if (TextUtils.isEmpty(mimeType)) {
newFile.setMimeType(MimeTypeUtil.getBestMimeTypeByFilename(localPath));
} else {
newFile.setMimeType(mimeType);
}

return newFile;
return UploadFileResolver.obtainNewOCFileToUpload(remotePath, localPath, mimeType);
}

public UploadFileOperation(UploadsStorageManager uploadsStorageManager,
Expand Down Expand Up @@ -281,6 +260,7 @@ public UploadFileOperation(UploadsStorageManager uploadsStorageManager,
mIgnoringPowerSaveMode = mCreatedBy == CREATED_BY_USER;
mFolderUnlockToken = upload.getFolderUnlockToken();
mDisableRetries = disableRetries;
uploadFileResolver = new UploadFileResolver(this);
}

public boolean isWifiRequired() {
Expand Down Expand Up @@ -384,6 +364,10 @@ public long getOCUploadId() {
return mOCUploadId;
}

public UploadsStorageManager getUploadsStorageManager() {
return uploadsStorageManager;
}

public Set<OnDatatransferProgressListener> getDataTransferListeners() {
return mDataTransferListeners;
}
Expand Down Expand Up @@ -452,7 +436,7 @@ protected RemoteOperationResult run(OwnCloudClient client) {
mPaused.set(false);
mUploadStarted.set(true);

updateSize(0);
uploadFileResolver.updateSize(0);
Log_OC.d(TAG, "file size set to 0KB before upload");

String remoteParentPath = new File(getRemotePath()).getParent();
Expand Down Expand Up @@ -564,7 +548,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare

E2EClientData clientData = new E2EClientData(client, token, publicKey);

List<String> fileNames = getCollidedFileNames(object);
List<String> fileNames = uploadFileResolver.getCollidedFileNames(object);

final var collisionResult = checkNameCollision(parentFile, client, fileNames, parentFile.isEncrypted());
if (collisionResult != null) {
Expand Down Expand Up @@ -601,7 +585,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare
channel = channelResult.getThird();

size = getChannelSize(channel);
updateSize(size);
uploadFileResolver.updateSize(size);
setUploadOperationForE2E(token, e2eFiles.getEncryptedTempFile(), e2eData.getEncryptedFileName(), lastModifiedTimestamp, creationTimestamp, size);

result = performE2EUpload(clientData);
Expand Down Expand Up @@ -667,23 +651,6 @@ private DecryptedFolderMetadataFileV1 getDecryptedFolderMetadataV1(String public
return metadata;
}

private List<String> getCollidedFileNames(Object object) {
List<String> result = new ArrayList<>();

if (object instanceof DecryptedFolderMetadataFileV1 metadata) {
for (DecryptedFile file : metadata.getFiles().values()) {
result.add(file.getEncrypted().getFilename());
}
} else if (object instanceof DecryptedFolderMetadataFile metadataFile) {
Map<String, com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile> files = metadataFile.getMetadata().getFiles();
for (com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile file : files.values()) {
result.add(file.getFilename());
}
}

return result;
}

private String getEncryptedFileName(Object object) {
String encryptedFileName = EncryptionUtils.generateUid();

Expand Down Expand Up @@ -1130,7 +1097,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) {
}

final var formattedFileSize = Formatter.formatFileSize(mContext, size);
updateSize(size);
uploadFileResolver.updateSize(size);
Log_OC.d(TAG, "file size set to " + formattedFileSize);

final long serverMaxChunkSize = getCapabilities().getChunkedUploadMaxSize();
Expand Down Expand Up @@ -1228,14 +1195,6 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) {
return result;
}

private void updateSize(long size) {
OCUpload ocUpload = uploadsStorageManager.getUploadById(getOCUploadId());
if (ocUpload != null) {
ocUpload.setFileSize(size);
uploadsStorageManager.updateUpload(ocUpload);
}
}

private void logResult(RemoteOperationResult<?> result, String sourcePath, String targetPath) {
if (result.isSuccess()) {
Log_OC.i(TAG, "Upload of " + sourcePath + " to " + targetPath + ": " + result.getLogMessage());
Expand Down Expand Up @@ -1296,7 +1255,11 @@ private RemoteOperationResult checkNameCollision(OCFile parentFile,
case RENAME:
mRemotePath = getNewAvailableRemotePath(client, mRemotePath, fileNames, encrypted);
mWasRenamed = true;
createNewOCFile(mRemotePath);

final var newFile = uploadFileResolver.createNewOCFile(mFile, mRemotePath);
mOldFile = mFile;
mFile = newFile;

Log_OC.d(TAG, "File renamed as " + mRemotePath);
if (mRenameUploadListener != null) {
mRenameUploadListener.onRenameUpload();
Expand Down Expand Up @@ -1448,7 +1411,7 @@ private RemoteOperationResult<?> grantFolderExistence(String pathToGrant, OwnClo
if (result.isSuccess()) {
OCFile parentDir = getStorageManager().getFileByPath(pathToGrant);
if (parentDir == null) {
parentDir = createLocalFolder(pathToGrant);
parentDir = uploadFileResolver.createLocalFolder(pathToGrant);
}
if (parentDir != null) {
result = new RemoteOperationResult<>(ResultCode.OK);
Expand All @@ -1459,50 +1422,6 @@ private RemoteOperationResult<?> grantFolderExistence(String pathToGrant, OwnClo
return result;
}

private OCFile createLocalFolder(String remotePath) {
String parentPath = new File(remotePath).getParent();
parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ?
parentPath : parentPath + OCFile.PATH_SEPARATOR;
OCFile parent = getStorageManager().getFileByPath(parentPath);
if (parent == null) {
parent = createLocalFolder(parentPath);
}
if (parent != null) {
OCFile createdFolder = new OCFile(remotePath);
createdFolder.setMimeType(MimeType.DIRECTORY);
createdFolder.setParentId(parent.getFileId());
getStorageManager().saveFile(createdFolder);
return createdFolder;
}
return null;
}


/**
* Create a new OCFile mFile with new remote path. This is required if nameCollisionPolicy==RENAME. New file is
* stored as mFile, original as mOldFile.
*
* @param newRemotePath new remote path
*/
private void createNewOCFile(String newRemotePath) {
// a new OCFile instance must be created for a new remote path
OCFile newFile = new OCFile(newRemotePath);
newFile.setCreationTimestamp(mFile.getCreationTimestamp());
newFile.setFileLength(mFile.getFileLength());
newFile.setMimeType(mFile.getMimeType());
newFile.setModificationTimestamp(mFile.getModificationTimestamp());
newFile.setModificationTimestampAtLastSyncForData(
mFile.getModificationTimestampAtLastSyncForData()
);
newFile.setEtag(mFile.getEtag());
newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
newFile.setStoragePath(mFile.getStoragePath());
newFile.setParentId(mFile.getParentId());
mOldFile = mFile;
mFile = newFile;
}

/**
* Returns a new and available (does not exists on the server) remotePath. This adds an incremental suffix.
*
Expand Down Expand Up @@ -1759,7 +1678,7 @@ private void saveUploadedFile(OwnCloudClient client) {
ReadFileRemoteOperation operation = new ReadFileRemoteOperation(path);
RemoteOperationResult result = operation.execute(client);
if (result.isSuccess()) {
updateOCFile(file, (RemoteFile) result.getData().get(0));
uploadFileResolver.updateOCFile(file, (RemoteFile) result.getData().get(0));
file.setLastSyncDateForProperties(syncDate);
} else {
Log_OC.e(TAG, "Error reading properties of file after successful upload; this is gonna hurt...");
Expand Down Expand Up @@ -1790,24 +1709,7 @@ private void saveUploadedFile(OwnCloudClient client) {
task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId()));
}

private void updateOCFile(OCFile file, RemoteFile remoteFile) {
file.setCreationTimestamp(remoteFile.getCreationTimestamp());
file.setFileLength(remoteFile.getLength());
file.setMimeType(remoteFile.getMimeType());
file.setModificationTimestamp(remoteFile.getModifiedTimestamp());
file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp());
file.setEtag(remoteFile.getEtag());
file.setEtagOnServer(remoteFile.getEtag());
file.setRemoteId(remoteFile.getRemoteId());
file.setPermissions(remoteFile.getPermissions());
file.setUploadTimestamp(remoteFile.getUploadTimestamp());
file.setPreviewAvailable(remoteFile.isHasPreview());
file.setImageDimension(remoteFile.getImageDimension());
file.setGeoLocation(remoteFile.getGeoLocation());
}

public interface OnRenameListener {

void onRenameUpload();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.owncloud.android.operations.upload

import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFolderMetadataFile
import com.owncloud.android.lib.resources.files.model.RemoteFile
import com.owncloud.android.operations.UploadFileOperation
import com.owncloud.android.utils.MimeType
import com.owncloud.android.utils.MimeTypeUtil
import java.io.File

class UploadFileResolver(private val operation: UploadFileOperation) {

companion object {
@JvmStatic
fun obtainNewOCFileToUpload(remotePath: String?, localPath: String?, mimeType: String?): OCFile =
OCFile(remotePath).apply {
setStoragePath(localPath)
lastSyncDateForProperties = 0
lastSyncDateForData = 0
if (!localPath.isNullOrEmpty()) {
val localFile = File(localPath)
fileLength = localFile.length()
lastSyncDateForData = localFile.lastModified()
}
setMimeType(
if (mimeType.isNullOrEmpty()) MimeTypeUtil.getBestMimeTypeByFilename(localPath) else mimeType
)
}
}

@Suppress("DEPRECATION")
fun getCollidedFileNames(metadata: Any?): List<String> = when (metadata) {
is DecryptedFolderMetadataFileV1 -> metadata.files.values.map { it.encrypted.filename }
is DecryptedFolderMetadataFile -> metadata.metadata.files.values.map { it.filename }
else -> emptyList()
}

fun updateSize(size: Long) {
val storageManager = operation.uploadsStorageManager
storageManager.getUploadById(operation.ocUploadId)?.let {
it.fileSize = size
storageManager.updateUpload(it)
}
}

fun createNewOCFile(fileToBeUploaded: OCFile, newRemotePath: String): OCFile = OCFile(newRemotePath).apply {
creationTimestamp = fileToBeUploaded.creationTimestamp
fileLength = fileToBeUploaded.fileLength
mimeType = fileToBeUploaded.mimeType
modificationTimestamp = fileToBeUploaded.modificationTimestamp
modificationTimestampAtLastSyncForData = fileToBeUploaded.modificationTimestampAtLastSyncForData
etag = fileToBeUploaded.etag
lastSyncDateForProperties = fileToBeUploaded.lastSyncDateForProperties
lastSyncDateForData = fileToBeUploaded.lastSyncDateForData
storagePath = fileToBeUploaded.storagePath
parentId = fileToBeUploaded.parentId
}

fun createLocalFolder(remotePath: String): OCFile? {
val storageManager = operation.storageManager
val parent = File(remotePath).parent
?.let { if (it.endsWith(OCFile.PATH_SEPARATOR)) it else it + OCFile.PATH_SEPARATOR }
?.let { storageManager.getFileByPath(it) ?: createLocalFolder(it) }
?: return null

return OCFile(remotePath).apply {
mimeType = MimeType.DIRECTORY
parentId = parent.fileId
}.also { storageManager.saveFile(it) }
}

fun updateOCFile(file: OCFile, remoteFile: RemoteFile) {
file.apply {
creationTimestamp = remoteFile.creationTimestamp
fileLength = remoteFile.length
mimeType = remoteFile.mimeType
modificationTimestamp = remoteFile.modifiedTimestamp
modificationTimestampAtLastSyncForData = remoteFile.modifiedTimestamp
etag = remoteFile.etag
etagOnServer = remoteFile.etag
remoteId = remoteFile.remoteId
permissions = remoteFile.permissions
uploadTimestamp = remoteFile.uploadTimestamp
isPreviewAvailable = remoteFile.isHasPreview
imageDimension = remoteFile.imageDimension
geoLocation = remoteFile.geoLocation
}
}
}
Loading
Loading