diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt index e59388bd..dd7d0a56 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license -* Copyright (C) 2020 ownCloud GmbH. +* Copyright (C) 2023 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,6 +41,7 @@ import java.util.concurrent.TimeUnit * @author David A. Velasco * @author David González Verdugo * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón * * @param remotePath Path to append to the URL owned by the client instance. * @param isUserLoggedIn When `true`, the username won't be added at the end of the PROPFIND url since is not @@ -48,21 +49,22 @@ import java.util.concurrent.TimeUnit */ class CheckPathExistenceRemoteOperation( val remotePath: String? = "", - val isUserLoggedIn: Boolean + val isUserLoggedIn: Boolean, + val spaceWebDavUrl: String? = null, ) : RemoteOperation() { override fun run(client: OwnCloudClient): RemoteOperationResult { - return try { - val stringUrl = - if (isUserLoggedIn) client.baseFilesWebDavUri.toString() - else client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath) + val baseStringUrl = spaceWebDavUrl ?: if (isUserLoggedIn) client.baseFilesWebDavUri.toString() + else client.userFilesWebDavUri.toString() + val stringUrl = baseStringUrl + WebdavUtils.encodePath(remotePath) + return try { val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropset).apply { setReadTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) } - var status = client.executeHttpMethod(propFindMethod) + val status = client.executeHttpMethod(propFindMethod) /* PROPFIND method * 404 NOT FOUND: path doesn't exist, * 207 MULTI_STATUS: path exists. @@ -77,7 +79,7 @@ class CheckPathExistenceRemoteOperation( val result = RemoteOperationResult(e) Timber.e( e, - "Existence check for ${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)} : ${result.logMessage}" + "Existence check for $stringUrl : ${result.logMessage}" ) result } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt index ce47a4fd..949d72e9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2022 ownCloud GmbH. + * Copyright (C) 2023 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -44,25 +44,29 @@ import java.util.concurrent.TimeUnit * @author David A. Velasco * @author Christian Schabesberger * @author David González V. + * @author Juan Carlos Garrote Gascón * - * @param srcRemotePath Remote path of the file/folder to copy. + * @param sourceRemotePath Remote path of the file/folder to copy. * @param targetRemotePath Remote path desired for the file/folder to copy it. */ class CopyRemoteFileOperation( - private val srcRemotePath: String, + private val sourceRemotePath: String, private val targetRemotePath: String, + private val sourceSpaceWebDavUrl: String? = null, + private val targetSpaceWebDavUrl: String? = null, ) : RemoteOperation() { + /** * Performs the rename operation. * * @param client Client object to communicate with the remote ownCloud server. */ override fun run(client: OwnCloudClient): RemoteOperationResult { - if (targetRemotePath == srcRemotePath) { + if (targetRemotePath == sourceRemotePath && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) { // nothing to do! return RemoteOperationResult(ResultCode.OK) } - if (targetRemotePath.startsWith(srcRemotePath)) { + if (targetRemotePath.startsWith(sourceRemotePath) && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) { return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT) } @@ -70,8 +74,8 @@ class CopyRemoteFileOperation( var result: RemoteOperationResult try { val copyMethod = CopyMethod( - URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(srcRemotePath)), - client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath), + URL((sourceSpaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(sourceRemotePath)), + (targetSpaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(targetRemotePath), ).apply { setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS) setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS) @@ -95,10 +99,10 @@ class CopyRemoteFileOperation( client.exhaustResponse(copyMethod.getResponseBodyAsStream()) } } - Timber.i("Copy $srcRemotePath to $targetRemotePath: ${result.logMessage}") + Timber.i("Copy $sourceRemotePath to $targetRemotePath: ${result.logMessage}") } catch (e: Exception) { result = RemoteOperationResult(e) - Timber.e(e, "Copy $srcRemotePath to $targetRemotePath: ${result.logMessage}") + Timber.e(e, "Copy $sourceRemotePath to $targetRemotePath: ${result.logMessage}") } return result } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt index 372c8f21..15a9e8bc 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt @@ -46,7 +46,8 @@ import java.util.concurrent.TimeUnit class CreateRemoteFolderOperation( val remotePath: String, private val createFullPath: Boolean, - private val isChunksFolder: Boolean = false + private val isChunksFolder: Boolean = false, + val spaceWebDavUrl: String? = null, ) : RemoteOperation() { override fun run(client: OwnCloudClient): RemoteOperationResult { @@ -67,13 +68,13 @@ class CreateRemoteFolderOperation( var result: RemoteOperationResult try { val webDavUri = if (isChunksFolder) { - client.uploadsWebDavUri + client.uploadsWebDavUri.toString() } else { - client.userFilesWebDavUri + spaceWebDavUrl ?: client.userFilesWebDavUri.toString() } val mkCol = MkColMethod( - URL(webDavUri.toString() + WebdavUtils.encodePath(remotePath)) + URL(webDavUri + WebdavUtils.encodePath(remotePath)) ).apply { setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS) setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt index 80b9adc7..86fcde46 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt @@ -36,7 +36,6 @@ import java.io.BufferedInputStream import java.io.File import java.io.FileOutputStream import java.net.URL -import java.util.HashSet import java.util.concurrent.atomic.AtomicBoolean /** @@ -47,7 +46,8 @@ import java.util.concurrent.atomic.AtomicBoolean */ class DownloadRemoteFileOperation( private val remotePath: String, - localFolderPath: String + localFolderPath: String, + private val spaceWebDavUrl: String? = null, ) : RemoteOperation() { private val cancellationRequested = AtomicBoolean(false) @@ -84,7 +84,8 @@ class DownloadRemoteFileOperation( var bis: BufferedInputStream? = null var savedFile = false - val getMethod = GetMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath))) + val webDavUri = spaceWebDavUrl ?: client.userFilesWebDavUri.toString() + val getMethod = GetMethod(URL(webDavUri + WebdavUtils.encodePath(remotePath))) try { val status = client.executeHttpMethod(getMethod) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt index 5741766a..6689c5f9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2021 ownCloud GmbH. + * Copyright (C) 2023 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,17 +38,22 @@ import java.util.concurrent.TimeUnit /** * Remote operation moving a remote file or folder in the ownCloud server to a different folder - * in the same account. + * in the same account and space. * * Allows renaming the moving file/folder at the same time. * * @author David A. Velasco * @author David González Verdugo * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón + * + * @param sourceRemotePath Remote path of the file/folder to copy. + * @param targetRemotePath Remote path desired for the file/folder to copy it. */ open class MoveRemoteFileOperation( private val sourceRemotePath: String, private val targetRemotePath: String, + private val spaceWebDavUrl: String? = null, ) : RemoteOperation() { /** @@ -73,8 +78,8 @@ open class MoveRemoteFileOperation( // so this uri has to be customizable val srcWebDavUri = getSrcWebDavUriForClient(client) val moveMethod = MoveMethod( - url = URL(srcWebDavUri.toString() + WebdavUtils.encodePath(sourceRemotePath)), - destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath), + url = URL((spaceWebDavUrl ?: srcWebDavUri.toString()) + WebdavUtils.encodePath(sourceRemotePath)), + destinationUrl = (spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(targetRemotePath), ).apply { addRequestHeaders(this) setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt index 539ea92c..cf29b60e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt @@ -46,7 +46,10 @@ import java.util.concurrent.TimeUnit * @author David González Verdugo */ -class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation() { +class ReadRemoteFileOperation( + val remotePath: String, + val spaceWebDavUrl: String? = null, +) : RemoteOperation() { /** * Performs the read operation. @@ -57,7 +60,7 @@ class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation { try { val propFind = PropfindMethod( - url = URL("${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)}"), + url = getFinalWebDavUrl(), depth = DEPTH_0, propertiesToRequest = DavUtils.allPropset ).apply { @@ -69,10 +72,11 @@ class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation(RemoteOperationResult.ResultCode.OK).apply { @@ -88,6 +92,12 @@ class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation>() { /** @@ -61,7 +62,7 @@ class ReadRemoteFolderOperation( PropertyRegistry.register(OCShareTypes.Factory()) val propfindMethod = PropfindMethod( - URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)), + getFinalWebDavUrl(), DavConstants.DEPTH_1, DavUtils.allPropset ) @@ -71,12 +72,11 @@ class ReadRemoteFolderOperation( if (isSuccess(status)) { val mFolderAndFiles = ArrayList() - // parse data from remote folder - // TODO: Remove that !! val remoteFolder = RemoteFile.getRemoteFileFromDav( davResource = propfindMethod.root!!, userId = AccountUtils.getUserId(mAccount, mContext), - userName = mAccount.name + userName = mAccount.name, + spaceWebDavUrl = spaceWebDavUrl, ) mFolderAndFiles.add(remoteFolder) @@ -85,7 +85,8 @@ class ReadRemoteFolderOperation( val remoteFile = RemoteFile.getRemoteFileFromDav( davResource = resource, userId = AccountUtils.getUserId(mAccount, mContext), - userName = mAccount.name + userName = mAccount.name, + spaceWebDavUrl = spaceWebDavUrl, ) mFolderAndFiles.add(remoteFile) } @@ -107,5 +108,11 @@ class ReadRemoteFolderOperation( } } + private fun getFinalWebDavUrl(): URL { + val baseWebDavUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString() + + return URL(baseWebDavUrl + WebdavUtils.encodePath(remotePath)) + } + private fun isSuccess(status: Int): Boolean = status.isOneOf(HTTP_OK, HTTP_MULTI_STATUS) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt index 87a6663e..791a0765 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt @@ -26,6 +26,7 @@ package com.owncloud.android.lib.resources.files import android.net.Uri import android.os.Parcelable +import androidx.annotation.VisibleForTesting import at.bitfire.dav4jvm.PropStat import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.Response @@ -100,8 +101,13 @@ data class RemoteFile( const val MIME_DIR = "DIR" const val MIME_DIR_UNIX = "httpd/unix-directory" - fun getRemoteFileFromDav(davResource: Response, userId: String, userName: String): RemoteFile { - val remotePath = getRemotePathFromUrl(davResource.href, userId) + fun getRemoteFileFromDav( + davResource: Response, + userId: String, + userName: String, + spaceWebDavUrl: String? = null + ): RemoteFile { + val remotePath = getRemotePathFromUrl(davResource.href, userId, spaceWebDavUrl) val remoteFile = RemoteFile(remotePath = remotePath, owner = userName) val properties = getPropertiesEvenIfPostProcessing(davResource) @@ -164,15 +170,25 @@ data class RemoteFile( * Retrieves a relative path from a remote file url * * - * Example: url:port/remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt + * Example legacy: + * /remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt + * + * Example spaces: + * /dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$05bca744-d89f-4e9c-a990-25a0d7f03fe9/Documents/text.txt => /Documents/text.txt * * @param url remote file url * @param userId file owner + * @param spaceWebDavUrl custom web dav url for space * @return remote relative path of the file */ - private fun getRemotePathFromUrl(url: HttpUrl, userId: String): String { - val davFilesPath = OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId - val absoluteDavPath = Uri.decode(url.encodedPath) + @VisibleForTesting + fun getRemotePathFromUrl( + url: HttpUrl, + userId: String, + spaceWebDavUrl: String? = null, + ): String { + val davFilesPath = spaceWebDavUrl ?: (OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId) + val absoluteDavPath = if (spaceWebDavUrl != null) Uri.decode(url.toString()) else Uri.decode(url.encodedPath) val pathToOc = absoluteDavPath.split(davFilesPath).first() return absoluteDavPath.replace(pathToOc + davFilesPath, "") } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt index a451256a..b97b3d78 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt @@ -24,7 +24,6 @@ package com.owncloud.android.lib.resources.files -import android.net.Uri import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.http.HttpConstants.HTTP_NO_CONTENT import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK @@ -46,7 +45,8 @@ import java.net.URL * @author Abel García de Prada */ open class RemoveRemoteFileOperation( - private val remotePath: String + private val remotePath: String, + val spaceWebDavUrl: String? = null, ) : RemoteOperation() { override fun run(client: OwnCloudClient): RemoteOperationResult { @@ -54,7 +54,7 @@ open class RemoveRemoteFileOperation( try { val srcWebDavUri = getSrcWebDavUriForClient(client) val deleteMethod = DeleteMethod( - URL(srcWebDavUri.toString() + WebdavUtils.encodePath(remotePath)) + URL(srcWebDavUri + WebdavUtils.encodePath(remotePath)) ) val status = client.executeHttpMethod(deleteMethod) @@ -75,7 +75,7 @@ open class RemoveRemoteFileOperation( * For standard removals, we will use [OwnCloudClient.getUserFilesWebDavUri]. * In case we need a different source Uri, override this method. */ - open fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.userFilesWebDavUri + open fun getSrcWebDavUriForClient(client: OwnCloudClient): String = spaceWebDavUrl ?: client.userFilesWebDavUri.toString() private fun isSuccess(status: Int) = status.isOneOf(HTTP_OK, HTTP_NO_CONTENT) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt index 8f669c69..9a0a4b30 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt @@ -48,6 +48,7 @@ class RenameRemoteFileOperation( private val oldRemotePath: String, private val newName: String, isFolder: Boolean, + val spaceWebDavUrl: String? = null, ) : RemoteOperation() { private var newRemotePath: String @@ -75,8 +76,8 @@ class RenameRemoteFileOperation( } val moveMethod: MoveMethod = MoveMethod( - url = URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(oldRemotePath)), - destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(newRemotePath), + url = URL((spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(oldRemotePath)), + destinationUrl = (spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(newRemotePath), ).apply { setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.MILLISECONDS) setConnectionTimeout(RENAME_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt index 93a2357a..a863a37b 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2022 ownCloud GmbH. + * Copyright (C) 2023 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -48,6 +48,7 @@ import java.util.concurrent.atomic.AtomicBoolean * @author masensio * @author David González Verdugo * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón */ open class UploadFileFromFileSystemOperation( val localPath: String, @@ -55,6 +56,7 @@ open class UploadFileFromFileSystemOperation( val mimeType: String, val lastModifiedTimestamp: String, val requiredEtag: String?, + val spaceWebDavUrl: String? = null, ) : RemoteOperation() { protected val cancellationRequested = AtomicBoolean(false) @@ -97,7 +99,8 @@ open class UploadFileFromFileSystemOperation( synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) } } - putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)), fileRequestBody!!).apply { + val baseStringUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString() + putMethod = PutMethod(URL(baseStringUrl + WebdavUtils.encodePath(remotePath)), fileRequestBody!!).apply { retryOnConnectionFailure = false if (!requiredEtag.isNullOrBlank()) { addRequestHeader(HttpConstants.IF_MATCH_HEADER, requiredEtag) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.kt index 150a4dd1..b738dfbf 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.kt @@ -24,10 +24,9 @@ */ package com.owncloud.android.lib.resources.files.chunks -import android.net.Uri import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation class RemoveRemoteChunksFolderOperation(remotePath: String) : RemoveRemoteFileOperation(remotePath) { - override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri + override fun getSrcWebDavUriForClient(client: OwnCloudClient): String = client.uploadsWebDavUri.toString() } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index 51a85c46..1421648b 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. + * Copyright (C) 2023 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,18 +32,22 @@ interface FileService : Service { fun checkPathExistence( path: String, - isUserLogged: Boolean + isUserLogged: Boolean, + spaceWebDavUrl: String? = null, ): RemoteOperationResult fun copyFile( sourceRemotePath: String, targetRemotePath: String, + sourceSpaceWebDavUrl: String?, + targetSpaceWebDavUrl: String?, ): RemoteOperationResult fun createFolder( remotePath: String, createFullPath: Boolean, - isChunkFolder: Boolean = false + isChunkFolder: Boolean = false, + spaceWebDavUrl: String? = null, ): RemoteOperationResult fun downloadFile( @@ -54,18 +58,22 @@ interface FileService : Service { fun moveFile( sourceRemotePath: String, targetRemotePath: String, + spaceWebDavUrl: String?, ): RemoteOperationResult fun readFile( - remotePath: String + remotePath: String, + spaceWebDavUrl: String? = null, ): RemoteOperationResult fun refreshFolder( - remotePath: String + remotePath: String, + spaceWebDavUrl: String? = null, ): RemoteOperationResult> fun removeFile( - remotePath: String + remotePath: String, + spaceWebDavUrl: String? = null, ): RemoteOperationResult fun renameFile( @@ -73,5 +81,6 @@ interface FileService : Service { oldRemotePath: String, newName: String, isFolder: Boolean, + spaceWebDavUrl: String? = null, ): RemoteOperationResult } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index 32ac22ca..8ec44d46 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2022 ownCloud GmbH. + * Copyright (C) 2023 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -42,11 +42,13 @@ class OCFileService(override val client: OwnCloudClient) : FileService { override fun checkPathExistence( path: String, - isUserLogged: Boolean + isUserLogged: Boolean, + spaceWebDavUrl: String?, ): RemoteOperationResult = CheckPathExistenceRemoteOperation( remotePath = path, - isUserLoggedIn = isUserLogged + isUserLoggedIn = isUserLogged, + spaceWebDavUrl = spaceWebDavUrl, ).execute(client) override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult = @@ -54,22 +56,28 @@ class OCFileService(override val client: OwnCloudClient) : FileService { override fun copyFile( sourceRemotePath: String, - targetRemotePath: String + targetRemotePath: String, + sourceSpaceWebDavUrl: String?, + targetSpaceWebDavUrl: String?, ): RemoteOperationResult = CopyRemoteFileOperation( - srcRemotePath = sourceRemotePath, - targetRemotePath = targetRemotePath + sourceRemotePath = sourceRemotePath, + targetRemotePath = targetRemotePath, + sourceSpaceWebDavUrl = sourceSpaceWebDavUrl, + targetSpaceWebDavUrl = targetSpaceWebDavUrl, ).execute(client) override fun createFolder( remotePath: String, createFullPath: Boolean, - isChunkFolder: Boolean + isChunkFolder: Boolean, + spaceWebDavUrl: String?, ): RemoteOperationResult = CreateRemoteFolderOperation( remotePath = remotePath, createFullPath = createFullPath, - isChunksFolder = isChunkFolder + isChunksFolder = isChunkFolder, + spaceWebDavUrl = spaceWebDavUrl, ).execute(client) override fun downloadFile( @@ -84,43 +92,53 @@ class OCFileService(override val client: OwnCloudClient) : FileService { override fun moveFile( sourceRemotePath: String, targetRemotePath: String, + spaceWebDavUrl: String?, ): RemoteOperationResult = MoveRemoteFileOperation( sourceRemotePath = sourceRemotePath, targetRemotePath = targetRemotePath, + spaceWebDavUrl = spaceWebDavUrl, ).execute(client) override fun readFile( - remotePath: String + remotePath: String, + spaceWebDavUrl: String?, ): RemoteOperationResult = ReadRemoteFileOperation( - remotePath = remotePath + remotePath = remotePath, + spaceWebDavUrl = spaceWebDavUrl, ).execute(client) override fun refreshFolder( - remotePath: String + remotePath: String, + spaceWebDavUrl: String?, ): RemoteOperationResult> = ReadRemoteFolderOperation( - remotePath = remotePath + remotePath = remotePath, + spaceWebDavUrl = spaceWebDavUrl, ).execute(client) override fun removeFile( - remotePath: String + remotePath: String, + spaceWebDavUrl: String?, ): RemoteOperationResult = RemoveRemoteFileOperation( - remotePath = remotePath + remotePath = remotePath, + spaceWebDavUrl = spaceWebDavUrl, ).execute(client) override fun renameFile( oldName: String, oldRemotePath: String, newName: String, - isFolder: Boolean + isFolder: Boolean, + spaceWebDavUrl: String?, ): RemoteOperationResult = RenameRemoteFileOperation( oldName = oldName, oldRemotePath = oldRemotePath, newName = newName, - isFolder = isFolder + isFolder = isFolder, + spaceWebDavUrl = spaceWebDavUrl, ).execute(client) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/GetRemoteSpacesOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/GetRemoteSpacesOperation.kt new file mode 100644 index 00000000..35a6727b --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/GetRemoteSpacesOperation.kt @@ -0,0 +1,99 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.owncloud.android.lib.resources.spaces + +import android.net.Uri +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse +import com.owncloud.android.lib.resources.spaces.responses.SpacesResponseWrapper +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import timber.log.Timber +import java.net.URL + +class GetRemoteSpacesOperation : RemoteOperation>() { + override fun run(client: OwnCloudClient): RemoteOperationResult> { + val requestUri = buildRequestUri(client.baseUri) + + val getMethod = GetMethod(URL(requestUri.toString())) + + return try { + val status = client.executeHttpMethod(getMethod) + val response = getMethod.getResponseBodyAsString() + + if (isSuccess(status)) { + onRequestSuccessful(response) + } else { + onResultUnsuccessful(getMethod, response, status) + } + } catch (e: Exception) { + Timber.e(e, "Exception while getting remote shares") + RemoteOperationResult(e) + } + } + + private fun buildRequestUri(baseUri: Uri) = + baseUri.buildUpon() + .appendEncodedPath(GRAPH_API_PATH) + .appendEncodedPath(ENDPOINT_SPACES_LIST) + .build() + + private fun parseResponse(response: String): List { + val moshi = Moshi.Builder().build() + val adapter: JsonAdapter = moshi.adapter(SpacesResponseWrapper::class.java) + return adapter.fromJson(response)?.value ?: listOf() + } + + private fun onResultUnsuccessful( + method: GetMethod, + response: String?, + status: Int + ): RemoteOperationResult> { + Timber.e("Failed response while getting spaces for user") + if (response != null) { + Timber.e("*** status code: $status; response message: $response") + } else { + Timber.e("*** status code: $status") + } + return RemoteOperationResult(method) + } + + private fun onRequestSuccessful(response: String?): RemoteOperationResult> { + val result = RemoteOperationResult>(RemoteOperationResult.ResultCode.OK) + Timber.d("Successful response: $response") + result.data = response?.let { parseResponse(it) } ?: listOf() + Timber.d("*** Fetch of spaces completed and parsed to ${result.data}") + return result + } + + private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK + + companion object { + private const val GRAPH_API_PATH = "graph/v1.0" + private const val ENDPOINT_SPACES_LIST = "me/drives" + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt new file mode 100644 index 00000000..6ce50498 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt @@ -0,0 +1,126 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.owncloud.android.lib.resources.spaces.responses + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class SpacesResponseWrapper( + val value: List +) + +@JsonClass(generateAdapter = true) +data class SpaceResponse( + val description: String?, + val driveAlias: String, + val driveType: String, + val id: String, + val lastModifiedDateTime: String?, + val name: String, + val owner: OwnerResponse?, + val quota: QuotaResponse?, + val root: RootResponse, + val special: List?, + val webUrl: String, +) + +@JsonClass(generateAdapter = true) +data class OwnerResponse( + val user: UserResponse +) + +@JsonClass(generateAdapter = true) +data class QuotaResponse( + val remaining: Long?, + val state: String?, + val total: Long, + val used: Long?, +) + +@JsonClass(generateAdapter = true) +data class RootResponse( + val eTag: String?, + val id: String, + val permissions: List?, + val webDavUrl: String, + val deleted: DeleteResponse?, +) + +@JsonClass(generateAdapter = true) +data class SpecialResponse( + val eTag: String, + val file: FileResponse, + val id: String, + val lastModifiedDateTime: String, + val name: String, + val size: Int, + val specialFolder: SpecialFolderResponse, + val webDavUrl: String +) + +@JsonClass(generateAdapter = true) +data class UserResponse( + val id: String +) + +@JsonClass(generateAdapter = true) +data class FileResponse( + val mimeType: String +) + +@JsonClass(generateAdapter = true) +data class IdentityPermissionResponse( + val id: String, + val displayName: String?, +) + +@JsonClass(generateAdapter = true) +data class GrantedToIdentitiesResponse( + val user: IdentityPermissionResponse?, + val group: IdentityPermissionResponse?, +) + +@JsonClass(generateAdapter = true) +data class DeleteResponse( + val state: String, +) + +@JsonClass(generateAdapter = true) +data class PermissionResponse( + val grantedTo: List?, + val grantedToIdentities: List?, + val roles: List, +) { + /** + * Supports api renaming from grantedTo to grantedToIdentities on v1.0.1 + * https://github.com/owncloud/libre-graph-api/releases/tag/v1.0.1 + */ + fun getGrantedToIdentitiesResponse(): List { + return grantedToIdentities ?: grantedTo ?: throw IllegalArgumentException("Permissions not granted to anyone") + } +} + +@JsonClass(generateAdapter = true) +data class SpecialFolderResponse( + val name: String +) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/OCSpacesService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/OCSpacesService.kt new file mode 100644 index 00000000..2f29601d --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/OCSpacesService.kt @@ -0,0 +1,34 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.owncloud.android.lib.resources.spaces.services + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.spaces.GetRemoteSpacesOperation +import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse + +class OCSpacesService(override val client: OwnCloudClient) : SpacesService { + override fun getSpaces(): RemoteOperationResult> { + return GetRemoteSpacesOperation().execute(client) + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/SpacesService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/SpacesService.kt new file mode 100644 index 00000000..2384769c --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/SpacesService.kt @@ -0,0 +1,31 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.owncloud.android.lib.resources.spaces.services + +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.Service +import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse + +interface SpacesService : Service { + fun getSpaces(): RemoteOperationResult> +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteCapabilitiesOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteCapabilitiesOperation.kt index 8591a68a..dd48376e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteCapabilitiesOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteCapabilitiesOperation.kt @@ -45,7 +45,7 @@ import java.net.URL /** * Get the Capabilities from the server - * Save in Result.getData in a RemoteCapability object + * Save Result.getData in a RemoteCapability object * * @author masensio * @author David González Verdugo diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/RemoteCapability.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/RemoteCapability.kt index ff7f5b4f..fe4c26f0 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/RemoteCapability.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/RemoteCapability.kt @@ -1,29 +1,33 @@ -/* ownCloud Android Library is available under MIT license - * @author masensio - * @author David González Verdugo - * @author Abel García de Prada - * Copyright (C) 2020 ownCloud GmbH. +/** + * ownCloud Android Library is available under MIT license * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * @author masensio + * @author David González Verdugo + * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * Copyright (C) 2022 ownCloud GmbH. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ + package com.owncloud.android.lib.resources.status /** @@ -33,7 +37,7 @@ data class RemoteCapability( var accountName: String = "", // Server version - var versionMayor: Int = 0, + var versionMajor: Int = 0, var versionMinor: Int = 0, var versionMicro: Int = 0, var versionString: String = "", @@ -68,7 +72,10 @@ data class RemoteCapability( var filesUndelete: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, var filesVersioning: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, val filesPrivateLinks: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, - val filesAppProviders: List?, + val filesAppProviders: List?, + + // Spaces + val spaces: RemoteSpaces?, ) { /** * Enum for Boolean Type in capabilities, with values: @@ -101,7 +108,7 @@ data class RemoteCapability( } } - data class RemoteOCISProvider( + data class RemoteAppProviders( val enabled: Boolean, val version: String, val appsUrl: String?, @@ -109,4 +116,10 @@ data class RemoteCapability( val openWebUrl: String?, val newUrl: String?, ) + + data class RemoteSpaces( + val enabled: Boolean, + val projects: Boolean, + val shareJail: Boolean, + ) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt index d4000b93..31673541 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt @@ -1,32 +1,35 @@ -/* ownCloud Android Library is available under MIT license - * @author Abel García de Prada - * Copyright (C) 2020 ownCloud GmbH. +/** + * ownCloud Android Library is available under MIT license * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * Copyright (C) 2022 ownCloud GmbH. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ + package com.owncloud.android.lib.resources.status.responses import com.owncloud.android.lib.resources.status.RemoteCapability -import com.owncloud.android.lib.resources.status.RemoteCapability.CapabilityBooleanType -import com.owncloud.android.lib.resources.status.RemoteCapability.RemoteOCISProvider +import com.owncloud.android.lib.resources.status.RemoteCapability.* import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -37,7 +40,7 @@ data class CapabilityResponse( val capabilities: Capabilities? ) { fun toRemoteCapability(): RemoteCapability = RemoteCapability( - versionMayor = serverVersion?.major ?: 0, + versionMajor = serverVersion?.major ?: 0, versionMinor = serverVersion?.minor ?: 0, versionMicro = serverVersion?.micro ?: 0, versionString = serverVersion?.string ?: "", @@ -70,10 +73,11 @@ data class CapabilityResponse( filesUndelete = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.undelete), filesVersioning = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.versioning), filesPrivateLinks = capabilities?.fileCapabilities?.privateLinks?.let { CapabilityBooleanType.fromBooleanValue(it) } ?: CapabilityBooleanType.UNKNOWN, - filesAppProviders = capabilities?.fileCapabilities?.appProviders?.map { it.toOCISProvider() }, + filesAppProviders = capabilities?.fileCapabilities?.appProviders?.map { it.toAppProviders() }, filesSharingFederationIncoming = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.incoming), filesSharingFederationOutgoing = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.outgoing), filesSharingUserProfilePicture = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingUser?.profilePicture), + spaces = capabilities?.spacesCapabilities?.toSpaces(), ) } @@ -86,7 +90,9 @@ data class Capabilities( @Json(name = "files") val fileCapabilities: FileCapabilities?, @Json(name = "dav") - val davCapabilities: DavCapabilities? + val davCapabilities: DavCapabilities?, + @Json(name = "spaces") + val spacesCapabilities: SpacesCapabilities?, ) @JsonClass(generateAdapter = true) @@ -182,7 +188,7 @@ data class AppProvider( @Json(name = "new_url") val newUrl: String?, ) { - fun toOCISProvider() = RemoteOCISProvider(enabled, version, appsUrl, openUrl, openWebUrl, newUrl) + fun toAppProviders() = RemoteAppProviders(enabled, version, appsUrl, openUrl, openWebUrl, newUrl) } @JsonClass(generateAdapter = true) @@ -190,6 +196,16 @@ data class DavCapabilities( val chunking: String? ) +@JsonClass(generateAdapter = true) +data class SpacesCapabilities( + val enabled: Boolean, + val projects: Boolean, + @Json(name = "share_jail") + val shareJail: Boolean, +) { + fun toSpaces() = RemoteSpaces(enabled, projects, shareJail) +} + @JsonClass(generateAdapter = true) data class ServerVersion( var major: Int?, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetInstancesViaWebFingerOperation.kt similarity index 77% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetInstancesViaWebFingerOperation.kt index 42ee4b12..2517b14b 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetInstancesViaWebFingerOperation.kt @@ -31,16 +31,16 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod import com.owncloud.android.lib.common.http.methods.nonwebdav.HttpMethod import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.webfinger.responses.WebfingerJrdResponse +import com.owncloud.android.lib.resources.webfinger.responses.WebFingerResponse import com.squareup.moshi.Moshi import timber.log.Timber import java.net.URL -class GetOCInstanceViaWebfingerOperation( +class GetInstancesViaWebFingerOperation( private val lockupServerDomain: String, private val rel: String, private val resource: String, -) : RemoteOperation() { +) : RemoteOperation>() { private fun buildRequestUri() = Uri.parse(lockupServerDomain).buildUpon() @@ -51,9 +51,9 @@ class GetOCInstanceViaWebfingerOperation( private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK - private fun parseResponse(response: String): WebfingerJrdResponse { + private fun parseResponse(response: String): WebFingerResponse { val moshi = Moshi.Builder().build() - val adapter = moshi.adapter(WebfingerJrdResponse::class.java) + val adapter = moshi.adapter(WebFingerResponse::class.java) return adapter.fromJson(response)!! } @@ -61,30 +61,25 @@ class GetOCInstanceViaWebfingerOperation( method: HttpMethod, response: String?, status: Int - ): RemoteOperationResult { - Timber.e("Failed requesting webfinger info") + ): RemoteOperationResult> { + Timber.e("Failed requesting WebFinger info") if (response != null) { Timber.e("*** status code: $status; response message: $response") } else { Timber.e("*** status code: $status") } - return RemoteOperationResult(method) + return RemoteOperationResult>(method) } - private fun onRequestSuccessful(rawResponse: String): RemoteOperationResult { + private fun onRequestSuccessful(rawResponse: String): RemoteOperationResult> { val response = parseResponse(rawResponse) - for (i in response.links) { - if (i.rel == rel) { - val operationResult = RemoteOperationResult(RemoteOperationResult.ResultCode.OK) - operationResult.data = i.href - return operationResult - } - } - Timber.e("Could not find ownCloud relevant information in webfinger response: $rawResponse") - throw java.lang.Exception("Could not find ownCloud relevant information in webfinger response") + Timber.d("Successful WebFinger request: $response") + val operationResult = RemoteOperationResult>(RemoteOperationResult.ResultCode.OK) + operationResult.data = response.links.map { it.href } + return operationResult } - override fun run(client: OwnCloudClient): RemoteOperationResult { + override fun run(client: OwnCloudClient): RemoteOperationResult> { val requestUri = buildRequestUri() val getMethod = GetMethod(URL(requestUri.toString())) return try { @@ -97,8 +92,8 @@ class GetOCInstanceViaWebfingerOperation( onResultUnsuccessful(getMethod, response, status) } } catch (e: Exception) { - Timber.e(e, "Requesting webfinger info failed") - RemoteOperationResult(e) + Timber.e(e, "Requesting WebFinger info failed") + RemoteOperationResult>(e) } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebFingerResponse.kt similarity index 96% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebFingerResponse.kt index 45e85a75..d7e5a6bb 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebFingerResponse.kt @@ -27,13 +27,13 @@ package com.owncloud.android.lib.resources.webfinger.responses import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class WebfingerJrdResponse( +data class WebFingerResponse( val subject: String, val links: List ) @JsonClass(generateAdapter = true) data class LinkItem( + val rel: String, val href: String, - val rel: String ) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/WebfingerService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/WebFingerService.kt similarity index 85% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/WebfingerService.kt rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/WebFingerService.kt index 4e057beb..f12e87fa 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/WebfingerService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/WebFingerService.kt @@ -20,10 +20,11 @@ package com.owncloud.android.lib.resources.webfinger.services import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult -interface WebfingerService { - fun getInstanceFromWebfinger( +interface WebFingerService { + fun getInstancesFromWebFinger( lookupServer: String, - username: String, + resource: String, + rel: String, client: OwnCloudClient, - ): RemoteOperationResult + ): RemoteOperationResult> } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/implementation/OCWebfingerService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/implementation/OCWebFingerService.kt similarity index 68% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/implementation/OCWebfingerService.kt rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/implementation/OCWebFingerService.kt index 3eab83d4..3f0ac4d9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/implementation/OCWebfingerService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/implementation/OCWebFingerService.kt @@ -19,20 +19,16 @@ package com.owncloud.android.lib.resources.webfinger.services.implementation import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.webfinger.GetOCInstanceViaWebfingerOperation -import com.owncloud.android.lib.resources.webfinger.services.WebfingerService +import com.owncloud.android.lib.resources.webfinger.GetInstancesViaWebFingerOperation +import com.owncloud.android.lib.resources.webfinger.services.WebFingerService -class OCWebfingerService : WebfingerService { +class OCWebFingerService : WebFingerService { - override fun getInstanceFromWebfinger( + override fun getInstancesFromWebFinger( lookupServer: String, - username: String, + resource: String, + rel: String, client: OwnCloudClient, - ): RemoteOperationResult = - GetOCInstanceViaWebfingerOperation(lookupServer, OWNCLOUD_REL, username).execute(client) - - companion object { - private const val OWNCLOUD_REL = "http://webfinger.owncloud/rel/server-instance" - } - + ): RemoteOperationResult> = + GetInstancesViaWebFingerOperation(lookupServer, rel, resource).execute(client) } diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/RemoteFileTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/RemoteFileTest.kt new file mode 100644 index 00000000..6cd8febe --- /dev/null +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/RemoteFileTest.kt @@ -0,0 +1,59 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2023 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib + +import android.os.Build +import com.owncloud.android.lib.resources.files.RemoteFile +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.O], manifest = Config.NONE) +class RemoteFileTest { + + @Test + fun getRemotePathFromUrl_legacyWebDav() { + val httpUrlToTest = "https://server.url/remote.php/dav/files/username/Documents/text.txt".toHttpUrl() + val expectedRemotePath = "/Documents/text.txt" + + val actualRemotePath = RemoteFile.Companion.getRemotePathFromUrl(httpUrlToTest, "username") + assertEquals(expectedRemotePath, actualRemotePath) + } + + @Test + fun getRemotePathFromUrl_spacesWebDav() { + val spaceWebDavUrl = "https://server.url/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$05bca744-d89f-4e9c-a990-25a0d7f03fe9" + + val httpUrlToTest = + "https://server.url/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$05bca744-d89f-4e9c-a990-25a0d7f03fe9/Documents/text.txt".toHttpUrl() + val expectedRemotePath = "/Documents/text.txt" + + val actualRemotePath = RemoteFile.Companion.getRemotePathFromUrl(httpUrlToTest, "username", spaceWebDavUrl) + assertEquals(expectedRemotePath, actualRemotePath) + } +} diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebFingerResponseTest.kt similarity index 92% rename from owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt rename to owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebFingerResponseTest.kt index 2d0c6235..bed95002 100644 --- a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebFingerResponseTest.kt @@ -8,15 +8,15 @@ import org.junit.Before import org.junit.Test import java.io.File -class WebfingerResponseTest { - lateinit var adapter: JsonAdapter +class WebFingerResponseTest { + lateinit var adapter: JsonAdapter private fun loadResponses(fileName: String) = adapter.fromJson(File(fileName).readText()) @Before fun prepare() { val moshi = Moshi.Builder().build() - adapter = moshi.adapter(WebfingerJrdResponse::class.java) + adapter = moshi.adapter(WebFingerResponse::class.java) } @Test