Browse Source

Merge pull request #12854 from nextcloud/bugfix/e2e-file-conflict

Bugfix e2e file conflict
Tobias Kaminsky 1 year ago
parent
commit
a8040e46f6

+ 32 - 0
app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt

@@ -0,0 +1,32 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.owncloud.android
+
+import com.owncloud.android.datamodel.OCFile
+import java.security.SecureRandom
+
+open class EncryptionIT : AbstractIT() {
+
+    fun testFolder(): OCFile {
+        val rootPath = "/"
+        val folderPath = "/TestFolder/"
+
+        OCFile(rootPath).apply {
+            storageManager.saveFile(this)
+        }
+
+        return OCFile(folderPath).apply {
+            decryptedRemotePath = folderPath
+            isEncrypted = true
+            fileLength = SecureRandom().nextLong()
+            setFolder()
+            parentId = storageManager.getFileByDecryptedRemotePath(rootPath)!!.fileId
+            storageManager.saveFile(this)
+        }
+    }
+}

+ 28 - 2
app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt

@@ -7,13 +7,18 @@
  */
 package com.owncloud.android.utils
 
-import com.owncloud.android.AbstractIT
+import com.owncloud.android.EncryptionIT
 import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
+import com.owncloud.android.datamodel.e2e.v1.decrypted.Data
+import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile
+import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1
+import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedMetadata
 import com.owncloud.android.lib.resources.e2ee.CsrHelper
+import com.owncloud.android.operations.RefreshFolderOperation
 import org.junit.Assert.assertEquals
 import org.junit.Test
 
-class EncryptionUtilsIT : AbstractIT() {
+class EncryptionUtilsIT : EncryptionIT() {
     @Throws(
         java.security.NoSuchAlgorithmException::class,
         java.io.IOException::class,
@@ -30,4 +35,25 @@ class EncryptionUtilsIT : AbstractIT() {
 
         assertEquals(key, EncryptionUtils.getPublicKey(user, e2eUser, arbitraryDataProvider))
     }
+
+    @Test
+    @Throws(Exception::class)
+    fun testUpdateFileNameForEncryptedFileV1() {
+        val folder = testFolder()
+
+        val decryptedFilename = "image.png"
+        val mockEncryptedFilename = "encrypted_file_name.png"
+
+        val decryptedMetadata = DecryptedMetadata()
+        val filesData = DecryptedFile().apply {
+            encrypted = Data().apply {
+                filename = decryptedFilename
+            }
+        }
+        val files = mapOf(mockEncryptedFilename to filesData)
+        val metadata = DecryptedFolderMetadataFileV1(decryptedMetadata, files)
+
+        RefreshFolderOperation.updateFileNameForEncryptedFileV1(storageManager, metadata, folder)
+        assertEquals(folder.decryptedRemotePath.contains("null"), false)
+    }
 }

+ 19 - 2
app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt

@@ -10,7 +10,7 @@ package com.owncloud.android.utils
 import com.google.gson.reflect.TypeToken
 import com.nextcloud.client.account.MockUser
 import com.nextcloud.common.User
-import com.owncloud.android.AbstractIT
+import com.owncloud.android.EncryptionIT
 import com.owncloud.android.datamodel.OCFile
 import com.owncloud.android.datamodel.e2e.v1.decrypted.Data
 import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1
@@ -21,13 +21,15 @@ import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedUser
 import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledrop
 import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledropUser
 import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFolderMetadataFile
+import com.owncloud.android.operations.RefreshFolderOperation
 import com.owncloud.android.util.EncryptionTestIT
 import junit.framework.TestCase.assertEquals
 import junit.framework.TestCase.assertTrue
 import org.junit.Assert.assertNotEquals
 import org.junit.Test
 
-class EncryptionUtilsV2IT : AbstractIT() {
+@Suppress("TooManyFunctions", "LargeClass")
+class EncryptionUtilsV2IT : EncryptionIT() {
     private val encryptionTestUtils = EncryptionTestUtils()
     private val encryptionUtilsV2 = EncryptionUtilsV2()
 
@@ -781,6 +783,21 @@ class EncryptionUtilsV2IT : AbstractIT() {
         assertTrue(encryptionUtilsV2.verifySignedMessage(base64Ans, jsonBase64, certs))
     }
 
+    @Test
+    @Throws(Exception::class)
+    fun testUpdateFileNameForEncryptedFile() {
+        val folder = testFolder()
+
+        val metadata = EncryptionTestUtils().generateFolderMetadataV2(
+            client.userId,
+            EncryptionTestIT.publicKey
+        )
+
+        RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, metadata, folder)
+
+        assertEquals(folder.decryptedRemotePath.contains("null"), false)
+    }
+
     /**
      * DecryptedFolderMetadata -> EncryptedFolderMetadata -> JSON -> encrypt -> decrypt -> JSON ->
      * EncryptedFolderMetadata -> DecryptedFolderMetadata

+ 55 - 0
app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -74,6 +74,7 @@ import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import kotlin.Pair;
 
 public class FileDataStorageManager {
     private static final String TAG = FileDataStorageManager.class.getSimpleName();
@@ -1737,6 +1738,7 @@ public class FileDataStorageManager {
         }
     }
 
+    @SuppressFBWarnings("PSC")
     public void saveConflict(OCFile ocFile, String etagInConflict) {
         ContentValues cv = new ContentValues();
         if (!ocFile.isDown()) {
@@ -2330,6 +2332,59 @@ public class FileDataStorageManager {
         }
     }
 
+    public String getFolderName(String path) {
+        return "/" + path.split("/")[1] + "/";
+    }
+
+    public String retrieveRemotePathConsideringEncryption(OCFile file) {
+        if (file == null) {
+            throw new NullPointerException("file cannot be null");
+        }
+
+        String remotePath = file.getRemotePath();
+        if (file.isEncrypted()) {
+            remotePath = getEncryptedRemotePath(file.getRemotePath());
+        }
+
+        return remotePath;
+    }
+
+    public String getEncryptedRemotePath(String decryptedRemotePath) {
+        String folderName = getFolderName(decryptedRemotePath);
+
+        if (folderName == null) {
+            throw new NullPointerException("folderName cannot be null");
+        }
+
+        OCFile folder = getFileByDecryptedRemotePath(folderName);
+        List<OCFile> files = getAllFilesRecursivelyInsideFolder(folder);
+        List<Pair<String, String>> decryptedFileNamesAndEncryptedRemotePaths = getDecryptedFileNamesAndEncryptedRemotePaths(files);
+
+        String decryptedFileName = decryptedRemotePath.substring( decryptedRemotePath.lastIndexOf('/') + 1);
+
+        for (Pair<String, String> item : decryptedFileNamesAndEncryptedRemotePaths) {
+            if (item.getFirst().equals(decryptedFileName)) {
+                return item.getSecond();
+            }
+        }
+
+        return null;
+    }
+
+    @SuppressFBWarnings("OCP")
+    private List<Pair<String, String>> getDecryptedFileNamesAndEncryptedRemotePaths(List<OCFile> fileList) {
+        List<Pair<String, String>> result = new ArrayList<>();
+
+        for (OCFile file : fileList) {
+            if (file.isEncrypted()) {
+                Pair<String, String> fileNameAndEncryptedRemotePath = new Pair<>(file.getDecryptedFileName(), file.getRemotePath());
+                result.add(fileNameAndEncryptedRemotePath);
+            }
+        }
+
+        return result;
+    }
+
     public void removeLocalFiles(User user, FileDataStorageManager storageManager) {
         File tempDir = new File(FileStorageUtils.getTemporalPath(user.getAccountName()));
         File saveDir = new File(FileStorageUtils.getSavePath(user.getAccountName()));

+ 119 - 96
app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java

@@ -58,15 +58,14 @@ import java.util.Vector;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR;
 
 /**
- *  Remote operation performing the synchronization of the list of files contained in a folder identified with its
- *  remote path.
- *  Fetches the list and properties of the files contained in the given folder, including their properties, and updates
- *  the local database with them.
- *  Does NOT enter in the child folders to synchronize their contents also.
+ * Remote operation performing the synchronization of the list of files contained in a folder identified with its remote
+ * path. Fetches the list and properties of the files contained in the given folder, including their properties, and
+ * updates the local database with them. Does NOT enter in the child folders to synchronize their contents also.
  */
 @SuppressWarnings("PMD.AvoidDuplicateLiterals")
 public class RefreshFolderOperation extends RemoteOperation {
@@ -74,37 +73,53 @@ public class RefreshFolderOperation extends RemoteOperation {
     private static final String TAG = RefreshFolderOperation.class.getSimpleName();
 
     public static final String EVENT_SINGLE_FOLDER_CONTENTS_SYNCED =
-            RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED";
+        RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED";
     public static final String EVENT_SINGLE_FOLDER_SHARES_SYNCED =
-            RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED";
+        RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED";
 
-    /** Time stamp for the synchronization process in progress */
+    /**
+     * Time stamp for the synchronization process in progress
+     */
     private long mCurrentSyncTime;
 
-    /** Remote folder to synchronize */
+    /**
+     * Remote folder to synchronize
+     */
     private OCFile mLocalFolder;
 
-    /** Access to the local database */
+    /**
+     * Access to the local database
+     */
     private FileDataStorageManager mStorageManager;
 
-    /** Account where the file to synchronize belongs */
+    /**
+     * Account where the file to synchronize belongs
+     */
     private User user;
 
-    /** Android context; necessary to send requests to the download service */
+    /**
+     * Android context; necessary to send requests to the download service
+     */
     private Context mContext;
 
-    /** Files and folders contained in the synchronized folder after a successful operation */
+    /**
+     * Files and folders contained in the synchronized folder after a successful operation
+     */
     private List<OCFile> mChildren;
 
-    /** Counter of conflicts found between local and remote files */
+    /**
+     * Counter of conflicts found between local and remote files
+     */
     private int mConflictsFound;
 
-    /** Counter of failed operations in synchronization of kept-in-sync files */
+    /**
+     * Counter of failed operations in synchronization of kept-in-sync files
+     */
     private int mFailsInKeptInSyncFound;
 
     /**
-     * Map of remote and local paths to files that where locally stored in a location
-     * out of the ownCloud folder and couldn't be copied automatically into it
+     * Map of remote and local paths to files that where locally stored in a location out of the ownCloud folder and
+     * couldn't be copied automatically into it
      **/
     private Map<String, String> mForgottenLocalFiles;
 
@@ -113,10 +128,14 @@ public class RefreshFolderOperation extends RemoteOperation {
      */
     private boolean mSyncFullAccount;
 
-    /** 'True' means that the remote folder changed and should be fetched */
+    /**
+     * 'True' means that the remote folder changed and should be fetched
+     */
     private boolean mRemoteFolderChanged;
 
-    /** 'True' means that Etag will be ignored */
+    /**
+     * 'True' means that Etag will be ignored
+     */
     private boolean mIgnoreETag;
 
     /**
@@ -131,16 +150,14 @@ public class RefreshFolderOperation extends RemoteOperation {
     /**
      * Creates a new instance of {@link RefreshFolderOperation}.
      *
-     * @param   folder                  Folder to synchronize.
-     * @param   currentSyncTime         Time stamp for the synchronization process in progress.
-     * @param   syncFullAccount         'True' means that this operation is part of a full account
-     *                                  synchronization.
-     * @param   ignoreETag              'True' means that the content of the remote folder should
-     *                                  be fetched and updated even though the 'eTag' did not
-     *                                  change.
-     * @param   dataStorageManager      Interface with the local database.
-     * @param   user                 ownCloud account where the folder is located.
-     * @param   context                 Application context.
+     * @param folder             Folder to synchronize.
+     * @param currentSyncTime    Time stamp for the synchronization process in progress.
+     * @param syncFullAccount    'True' means that this operation is part of a full account synchronization.
+     * @param ignoreETag         'True' means that the content of the remote folder should be fetched and updated even
+     *                           though the 'eTag' did not change.
+     * @param dataStorageManager Interface with the local database.
+     * @param user               ownCloud account where the folder is located.
+     * @param context            Application context.
      */
     public RefreshFolderOperation(OCFile folder,
                                   long currentSyncTime,
@@ -196,8 +213,8 @@ public class RefreshFolderOperation extends RemoteOperation {
     }
 
     /**
-     * Returns the list of files and folders contained in the synchronized folder,
-     * if called after synchronization is complete.
+     * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is
+     * complete.
      *
      * @return List of files and folders contained in the synchronized folder.
      */
@@ -207,7 +224,7 @@ public class RefreshFolderOperation extends RemoteOperation {
 
     /**
      * Performs the synchronization.
-     *
+     * <p>
      * {@inheritDoc}
      */
     @Override
@@ -246,7 +263,7 @@ public class RefreshFolderOperation extends RemoteOperation {
         if (!mSyncFullAccount && mRemoteFolderChanged) {
             sendLocalBroadcast(
                 EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result
-            );
+                              );
         }
 
         if (result.isSuccess() && !mSyncFullAccount && !mOnlyFileMetadata) {
@@ -256,7 +273,7 @@ public class RefreshFolderOperation extends RemoteOperation {
         if (!mSyncFullAccount) {
             sendLocalBroadcast(
                 EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result
-            );
+                              );
         }
 
         return result;
@@ -371,7 +388,7 @@ public class RefreshFolderOperation extends RemoteOperation {
             result = new RemoteOperationResult(ResultCode.OK);
 
             Log_OC.i(TAG, "Checked " + user.getAccountName() + remotePath + " : " +
-                    (mRemoteFolderChanged ? "changed" : "not changed"));
+                (mRemoteFolderChanged ? "changed" : "not changed"));
 
         } else {
             // check failed
@@ -380,10 +397,10 @@ public class RefreshFolderOperation extends RemoteOperation {
             }
             if (result.isException()) {
                 Log_OC.e(TAG, "Checked " + user.getAccountName() + remotePath + " : " +
-                        result.getLogMessage(), result.getException());
+                    result.getLogMessage(), result.getException());
             } else {
                 Log_OC.e(TAG, "Checked " + user.getAccountName() + remotePath + " : " +
-                        result.getLogMessage());
+                    result.getLogMessage());
             }
         }
 
@@ -425,9 +442,9 @@ public class RefreshFolderOperation extends RemoteOperation {
 
 
     /**
-     * Synchronizes the data retrieved from the server about the contents of the target folder
-     * with the current data in the local database.
-     *
+     * Synchronizes the data retrieved from the server about the contents of the target folder with the current data in
+     * the local database.
+     * <p>
      * Grants that mChildren is updated with fresh data after execution.
      *
      * @param folderAndFiles Remote folder and children files in Folder
@@ -553,6 +570,7 @@ public class RefreshFolderOperation extends RemoteOperation {
             updatedFiles.add(updatedFile);
         }
 
+
         // save updated contents in local database
         // update file name for encrypted files
         if (e2EVersion == E2EVersion.V1_2) {
@@ -584,6 +602,37 @@ public class RefreshFolderOperation extends RemoteOperation {
         return metadata;
     }
 
+    @SuppressFBWarnings("CE")
+    private static void setMimeTypeAndDecryptedRemotePath(OCFile updatedFile, FileDataStorageManager storageManager, String decryptedFileName, String mimetype) {
+        OCFile parentFile = storageManager.getFileById(updatedFile.getParentId());
+
+        if (parentFile == null) {
+            throw new NullPointerException("parentFile cannot be null");
+        }
+
+        String decryptedRemotePath;
+        if (decryptedFileName != null) {
+            decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName;
+        } else {
+            decryptedRemotePath = parentFile.getRemotePath() + updatedFile.getFileName();
+        }
+
+        if (updatedFile.isFolder()) {
+            decryptedRemotePath += "/";
+        }
+        updatedFile.setDecryptedRemotePath(decryptedRemotePath);
+
+        if (mimetype == null || mimetype.isEmpty()) {
+            if (updatedFile.isFolder()) {
+                updatedFile.setMimeType(MimeType.DIRECTORY);
+            } else {
+                updatedFile.setMimeType("application/octet-stream");
+            }
+        } else {
+            updatedFile.setMimeType(mimetype);
+        }
+    }
+
     public static void updateFileNameForEncryptedFileV1(FileDataStorageManager storageManager,
                                                         @NonNull DecryptedFolderMetadataFileV1 metadata,
                                                         OCFile updatedFile) {
@@ -597,28 +646,16 @@ public class RefreshFolderOperation extends RemoteOperation {
             } else {
                 com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile decryptedFile =
                     metadata.getFiles().get(updatedFile.getFileName());
-                decryptedFileName = decryptedFile.getEncrypted().getFilename();
-                mimetype = decryptedFile.getEncrypted().getMimetype();
-            }
-
 
-            OCFile parentFile = storageManager.getFileById(updatedFile.getParentId());
-            String decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName;
+                if (decryptedFile == null) {
+                    throw new NullPointerException("decryptedFile cannot be null");
+                }
 
-            if (updatedFile.isFolder()) {
-                decryptedRemotePath += "/";
+                decryptedFileName = decryptedFile.getEncrypted().getFilename();
+                mimetype = decryptedFile.getEncrypted().getMimetype();
             }
-            updatedFile.setDecryptedRemotePath(decryptedRemotePath);
 
-            if (mimetype == null || mimetype.isEmpty()) {
-                if (updatedFile.isFolder()) {
-                    updatedFile.setMimeType(MimeType.DIRECTORY);
-                } else {
-                    updatedFile.setMimeType("application/octet-stream");
-                }
-            } else {
-                updatedFile.setMimeType(mimetype);
-            }
+            setMimeTypeAndDecryptedRemotePath(updatedFile, storageManager, decryptedFileName, mimetype);
         } catch (NullPointerException e) {
             Log_OC.e(TAG, "DecryptedMetadata for file " + updatedFile.getFileId() + " not found!");
         }
@@ -636,28 +673,16 @@ public class RefreshFolderOperation extends RemoteOperation {
                 mimetype = MimeType.DIRECTORY;
             } else {
                 DecryptedFile decryptedFile = metadata.getMetadata().getFiles().get(updatedFile.getFileName());
-                decryptedFileName = decryptedFile.getFilename();
-                mimetype = decryptedFile.getMimetype();
-            }
-
 
-            OCFile parentFile = storageManager.getFileById(updatedFile.getParentId());
-            String decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName;
+                if (decryptedFile == null) {
+                    throw new NullPointerException("decryptedFile cannot be null");
+                }
 
-            if (updatedFile.isFolder()) {
-                decryptedRemotePath += "/";
+                decryptedFileName = decryptedFile.getFilename();
+                mimetype = decryptedFile.getMimetype();
             }
-            updatedFile.setDecryptedRemotePath(decryptedRemotePath);
 
-            if (mimetype == null || mimetype.isEmpty()) {
-                if (updatedFile.isFolder()) {
-                    updatedFile.setMimeType(MimeType.DIRECTORY);
-                } else {
-                    updatedFile.setMimeType("application/octet-stream");
-                }
-            } else {
-                updatedFile.setMimeType(mimetype);
-            }
+            setMimeTypeAndDecryptedRemotePath(updatedFile, storageManager, decryptedFileName, mimetype);
         } catch (NullPointerException e) {
             Log_OC.e(TAG, "DecryptedMetadata for file " + updatedFile.getFileId() + " not found!");
         }
@@ -668,8 +693,8 @@ public class RefreshFolderOperation extends RemoteOperation {
             updatedFile.setFileId(localFile.getFileId());
             updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
             updatedFile.setModificationTimestampAtLastSyncForData(
-                    localFile.getModificationTimestampAtLastSyncForData()
-            );
+                localFile.getModificationTimestampAtLastSyncForData()
+                                                                 );
             if (localFile.isEncrypted()) {
                 if (mLocalFolder.getStoragePath() == null) {
                     updatedFile.setStoragePath(FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mLocalFolder) +
@@ -685,7 +710,7 @@ public class RefreshFolderOperation extends RemoteOperation {
 
             // eTag will not be updated unless file CONTENTS are synchronized
             if (!updatedFile.isFolder() && localFile.isDown() &&
-                    !updatedFile.getEtag().equals(localFile.getEtag())) {
+                !updatedFile.getEtag().equals(localFile.getEtag())) {
                 updatedFile.setEtagInConflict(updatedFile.getEtag());
             }
 
@@ -695,8 +720,8 @@ public class RefreshFolderOperation extends RemoteOperation {
                 updatedFile.setFileLength(remoteFile.getFileLength());
                 updatedFile.setMountType(remoteFile.getMountType());
             } else if (remoteFolderChanged && MimeTypeUtil.isImage(remoteFile) &&
-                    remoteFile.getModificationTimestamp() !=
-                            localFile.getModificationTimestamp()) {
+                remoteFile.getModificationTimestamp() !=
+                    localFile.getModificationTimestamp()) {
                 updatedFile.setUpdateThumbnailNeeded(true);
                 Log_OC.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
             }
@@ -713,6 +738,7 @@ public class RefreshFolderOperation extends RemoteOperation {
     }
 
     @NonNull
+    @SuppressFBWarnings("OCP")
     public static Map<String, OCFile> prefillLocalFilesMap(Object metadata, List<OCFile> localFiles) {
         Map<String, OCFile> localFilesMap = Maps.newHashMapWithExpectedSize(localFiles.size());
 
@@ -731,13 +757,12 @@ public class RefreshFolderOperation extends RemoteOperation {
     }
 
     /**
-     * Performs a list of synchronization operations, determining if a download or upload is needed
-     * or if exists conflict due to changes both in local and remote contents of the each file.
-     *
-     * If download or upload is needed, request the operation to the corresponding service and goes
-     * on.
+     * Performs a list of synchronization operations, determining if a download or upload is needed or if exists
+     * conflict due to changes both in local and remote contents of the each file.
+     * <p>
+     * If download or upload is needed, request the operation to the corresponding service and goes on.
      *
-     * @param filesToSyncContents       Synchronization operations to execute.
+     * @param filesToSyncContents Synchronization operations to execute.
      */
     private void startContentSynchronizations(List<SynchronizeFileOperation> filesToSyncContents) {
         RemoteOperationResult contentsResult;
@@ -750,10 +775,10 @@ public class RefreshFolderOperation extends RemoteOperation {
                     mFailsInKeptInSyncFound++;
                     if (contentsResult.getException() != null) {
                         Log_OC.e(TAG, "Error while synchronizing favourites : "
-                                + contentsResult.getLogMessage(), contentsResult.getException());
+                            + contentsResult.getLogMessage(), contentsResult.getException());
                     } else {
                         Log_OC.e(TAG, "Error while synchronizing favourites : "
-                                + contentsResult.getLogMessage());
+                            + contentsResult.getLogMessage());
                     }
                 }
             }   // won't let these fails break the synchronization process
@@ -763,9 +788,9 @@ public class RefreshFolderOperation extends RemoteOperation {
     /**
      * Syncs the Share resources for the files contained in the folder refreshed (children, not deeper descendants).
      *
-     * @param client    Handler of a session with an OC server.
-     * @return The result of the remote operation retrieving the Share resources in the folder refreshed by
-     *                  the operation.
+     * @param client Handler of a session with an OC server.
+     * @return The result of the remote operation retrieving the Share resources in the folder refreshed by the
+     * operation.
      */
     private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) {
         RemoteOperationResult result;
@@ -793,12 +818,10 @@ public class RefreshFolderOperation extends RemoteOperation {
     }
 
     /**
-     * Sends a message to any application component interested in the progress
-     * of the synchronization.
+     * Sends a message to any application component interested in the progress of the synchronization.
      *
      * @param event         broadcast event (Intent Action)
-     * @param dirRemotePath Remote path of a folder that was just synchronized
-     *                      (with or without success)
+     * @param dirRemotePath Remote path of a folder that was just synchronized (with or without success)
      * @param result        remote operation result
      */
     private void sendLocalBroadcast(String event, String dirRemotePath, RemoteOperationResult result) {

+ 12 - 6
app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt

@@ -21,6 +21,7 @@ import com.nextcloud.client.jobs.upload.UploadNotificationManager
 import com.nextcloud.model.HTTPStatusCodes
 import com.nextcloud.utils.extensions.getParcelableArgument
 import com.owncloud.android.R
+import com.owncloud.android.datamodel.FileDataStorageManager
 import com.owncloud.android.datamodel.OCFile
 import com.owncloud.android.datamodel.UploadsStorageManager
 import com.owncloud.android.db.OCUpload
@@ -42,6 +43,10 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
     @Inject
     var uploadsStorageManager: UploadsStorageManager? = null
 
+    @JvmField
+    @Inject
+    var fileStorageManager: FileDataStorageManager? = null
+
     private var conflictUploadId: Long = 0
     private var existingFile: OCFile? = null
     private var newFile: OCFile? = null
@@ -159,8 +164,8 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
             return
         }
         if (existingFile == null) {
-            // fetch info of existing file from server
-            val operation = ReadFileRemoteOperation(newFile!!.remotePath)
+            val remotePath = fileStorageManager?.retrieveRemotePathConsideringEncryption(newFile) ?: return
+            val operation = ReadFileRemoteOperation(remotePath)
 
             @Suppress("TooGenericExceptionCaught")
             Thread {
@@ -169,7 +174,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
                     if (result.isSuccess) {
                         existingFile = FileStorageUtils.fillOCFile(result.data[0] as RemoteFile)
                         existingFile?.lastSyncDateForProperties = System.currentTimeMillis()
-                        startDialog()
+                        startDialog(remotePath)
                     } else {
                         Log_OC.e(TAG, "ReadFileRemoteOp returned failure with code: " + result.httpCode)
                         showErrorAndFinish(result.httpCode)
@@ -180,11 +185,12 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
                 }
             }.start()
         } else {
-            startDialog()
+            val remotePath = fileStorageManager?.retrieveRemotePathConsideringEncryption(existingFile) ?: return
+            startDialog(remotePath)
         }
     }
 
-    private fun startDialog() {
+    private fun startDialog(remotePath: String) {
         val userOptional = user
         if (!userOptional.isPresent) {
             Log_OC.e(TAG, "User not present")
@@ -197,7 +203,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
         if (prev != null) {
             fragmentTransaction.remove(prev)
         }
-        if (existingFile != null && storageManager.fileExists(newFile?.remotePath)) {
+        if (existingFile != null && storageManager.fileExists(remotePath)) {
             val dialog = ConflictsResolveDialog.newInstance(
                 existingFile,
                 newFile,

+ 12 - 7
app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -54,7 +54,6 @@ import com.nextcloud.client.jobs.download.FileDownloadHelper;
 import com.nextcloud.client.jobs.download.FileDownloadWorker;
 import com.nextcloud.client.jobs.upload.FileUploadHelper;
 import com.nextcloud.client.jobs.upload.FileUploadWorker;
-import com.nextcloud.client.jobs.upload.UploadNotificationManager;
 import com.nextcloud.client.media.PlayerServiceConnection;
 import com.nextcloud.client.network.ClientFactory;
 import com.nextcloud.client.network.ConnectivityService;
@@ -878,18 +877,24 @@ public class FileDisplayActivity extends FileActivity
         requestUploadOfFilesFromFileSystem(basePath, filePaths, resultCode);
     }
 
+    private String[] getRemotePaths(String directory, String[] filePaths, String localBasePath) {
+        String[] remotePaths = new String[filePaths.length];
+        for (int j = 0; j < remotePaths.length; j++) {
+            String relativePath = StringUtils.removePrefix(filePaths[j], localBasePath);
+            remotePaths[j] = directory + relativePath;
+        }
+
+        return remotePaths;
+    }
+
     private void requestUploadOfFilesFromFileSystem(String localBasePath, String[] filePaths, int resultCode) {
         if (localBasePath != null && filePaths != null) {
             if (!localBasePath.endsWith("/")) {
                 localBasePath = localBasePath + "/";
             }
 
-            String[] remotePaths = new String[filePaths.length];
             String remotePathBase = getCurrentDir().getRemotePath();
-            for (int j = 0; j < remotePaths.length; j++) {
-                String relativePath = StringUtils.removePrefix(filePaths[j], localBasePath);
-                remotePaths[j] = remotePathBase + relativePath;
-            }
+            String[] decryptedRemotePaths = getRemotePaths(remotePathBase, filePaths, localBasePath);
 
             int behaviour = switch (resultCode) {
                 case UploadFilesActivity.RESULT_OK_AND_MOVE -> FileUploadWorker.LOCAL_BEHAVIOUR_MOVE;
@@ -899,7 +904,7 @@ public class FileDisplayActivity extends FileActivity
 
             FileUploadHelper.Companion.instance().uploadNewFiles(getUser().orElseThrow(RuntimeException::new),
                                                                  filePaths,
-                                                                 remotePaths,
+                                                                 decryptedRemotePaths,
                                                                  behaviour,
                                                                  true,
                                                                  UploadFileOperation.CREATED_BY_USER,

+ 1 - 1
app/src/main/res/values/strings.xml

@@ -997,7 +997,7 @@
     <string name="thumbnail_for_new_file_desc">Thumbnail for new file</string>
     <string name="thumbnail_for_existing_file_description">Thumbnail for existing file</string>
     <string name="invalid_url">Invalid URL</string>
-    <string name="conflict_dialog_error">Error creating conflict dialog!</string>
+    <string name="conflict_dialog_error">Conflict resolver dialog cannot able to created</string>
     <string name="qr_could_not_be_read">QR code could not be read!</string>
     <string name="note_icon_hint">Note icon</string>
     <string name="add_another_public_share_link">Add another link</string>