diff --git a/build.gradle b/build.gradle index aa8824db..29330f17 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { - kotlinVersion = '1.6.21' - moshiVersion = "1.13.0" + orgJetbrainsKotlin = '1.8.10' + comSquareupMoshi = '1.14.0' } repositories { @@ -10,12 +10,16 @@ buildscript { maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath "org.jlleitschuh.gradle:ktlint-gradle:10.3.0" - classpath 'com.android.tools.build:gradle:7.1.3' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" + classpath "org.jlleitschuh.gradle:ktlint-gradle:11.1.0" + classpath 'com.android.tools.build:gradle:7.4.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$orgJetbrainsKotlin" } } +plugins { + id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false +} + allprojects { repositories { google() @@ -26,4 +30,5 @@ allprojects { subprojects { apply plugin: "org.jlleitschuh.gradle.ktlint" -} \ No newline at end of file + apply plugin: "com.google.devtools.ksp" +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..8049c684 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/owncloudComLibrary/build.gradle b/owncloudComLibrary/build.gradle index 34391930..e626c033 100644 --- a/owncloudComLibrary/build.gradle +++ b/owncloudComLibrary/build.gradle @@ -1,32 +1,32 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' +apply plugin: 'com.google.devtools.ksp' apply plugin: 'kotlin-parcelize' dependencies { api 'com.squareup.okhttp3:okhttp:4.6.0' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$orgJetbrainsKotlin" api 'com.gitlab.ownclouders:dav4android:oc_support_2.1.5' api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2' // Moshi - implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") { + implementation("com.squareup.moshi:moshi-kotlin:$comSquareupMoshi") { exclude module: "kotlin-reflect" } implementation 'org.apache.commons:commons-lang3:3.12.0' - kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" + ksp "com.squareup.moshi:moshi-kotlin-codegen:$comSquareupMoshi" testImplementation 'junit:junit:4.13.2' - testImplementation 'org.robolectric:robolectric:4.9' + testImplementation 'org.robolectric:robolectric:4.10' debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.6.0' } android { - compileSdkVersion 31 + compileSdkVersion 33 defaultConfig { minSdkVersion 21 - targetSdkVersion 31 + targetSdkVersion 33 } lint { @@ -39,4 +39,5 @@ android { includeAndroidResources = true } } + namespace 'com.owncloud.android.lib' } diff --git a/owncloudComLibrary/src/main/AndroidManifest.xml b/owncloudComLibrary/src/main/AndroidManifest.xml index 4561b3f5..baef71f2 100644 --- a/owncloudComLibrary/src/main/AndroidManifest.xml +++ b/owncloudComLibrary/src/main/AndroidManifest.xml @@ -23,8 +23,7 @@ --> - + diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index 23a5a92f..258c514c 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -37,7 +37,6 @@ import com.owncloud.android.lib.resources.status.RemoteServerInfo import org.apache.commons.lang3.exception.ExceptionUtils import timber.log.Timber import java.io.IOException -import java.lang.Exception /** * ConnectionValidator @@ -46,7 +45,7 @@ import java.lang.Exception */ class ConnectionValidator( val context: Context, - val clearCookiesOnValidation: Boolean + private val clearCookiesOnValidation: Boolean ) { fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager, context: Context): Boolean { try { @@ -61,12 +60,12 @@ class ConnectionValidator( client.account = baseClient.account client.credentials = baseClient.credentials while (validationRetryCount < VALIDATION_RETRY_COUNT) { - Timber.d("validationRetryCout %d", validationRetryCount) + Timber.d("validationRetryCount %d", validationRetryCount) var successCounter = 0 var failCounter = 0 client.setFollowRedirects(true) - if (isOnwCloudStatusOk(client)) { + if (isOwnCloudStatusOk(client)) { successCounter++ } else { failCounter++ @@ -103,7 +102,7 @@ class ConnectionValidator( return false } - private fun isOnwCloudStatusOk(client: OwnCloudClient): Boolean { + private fun isOwnCloudStatusOk(client: OwnCloudClient): Boolean { val reply = getOwnCloudStatus(client) // dont check status code. It currently relais on the broken redirect code of the owncloud client // TODO: Use okhttp redirect and add this check again @@ -138,6 +137,12 @@ class ConnectionValidator( // test if have all the needed to effectively invalidate ... shouldInvalidateAccountCredentials = shouldInvalidateAccountCredentials and (account.savedAccount != null) + Timber.d( + """Received error: $httpStatusCode, + account: ${account.name} + credentials are real: ${credentials !is OwnCloudAnonymousCredentials}, + so we need to invalidate credentials for account ${account.name} : $shouldInvalidateAccountCredentials""" + ) return shouldInvalidateAccountCredentials } @@ -150,6 +155,7 @@ class ConnectionValidator( * */ private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) { + Timber.i("Invalidating account credentials for account $account") val am = AccountManager.get(context) am.invalidateAuthToken( account.savedAccount.type, @@ -167,8 +173,6 @@ class ConnectionValidator( * * Refresh current credentials if possible, and marks a retry. * - * @param status - * @param repeatCounter * @return */ private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean { @@ -181,6 +185,7 @@ class ConnectionValidator( if (credentials.authTokenCanBeRefreshed()) { try { // This command does the actual refresh + Timber.i("Trying to refresh auth token for account $account") account.loadCredentials(context) // if mAccount.getCredentials().length() == 0 --> refresh failed client.credentials = account.credentials @@ -201,6 +206,7 @@ class ConnectionValidator( if (!credentialsWereRefreshed) { // if credentials are not refreshed, client must be removed // from the OwnCloudClientManager to prevent it is reused once and again + Timber.w("Credentials were not refreshed, client will be removed from the Session Manager to prevent using it over and over") singleSessionManager.removeClientFor(account) } } @@ -210,6 +216,6 @@ class ConnectionValidator( } companion object { - private val VALIDATION_RETRY_COUNT = 3 + private const val VALIDATION_RETRY_COUNT = 3 } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 1281ec13..c8c3ca11 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -43,6 +43,7 @@ import timber.log.Timber; import java.io.IOException; import java.io.InputStream; import java.util.List; +import java.util.Locale; import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER; import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY; @@ -128,6 +129,7 @@ public class OwnCloudClient extends HttpClient { Timber.d("Executing in request with id %s", requestId); method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId); method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent()); + method.setRequestHeader(HttpConstants.ACCEPT_LANGUAGE_HEADER, Locale.getDefault().getLanguage()); method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY); if (mCredentials.getHeaderAuth() != null && !mCredentials.getHeaderAuth().isEmpty()) { method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java index 7e40c16f..9a7925ed 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java @@ -31,7 +31,6 @@ import android.net.Uri; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; -import com.owncloud.android.lib.common.http.HttpClient; import timber.log.Timber; import java.io.IOException; @@ -124,6 +123,24 @@ public class SingleSessionManager { } } else { Timber.v("reusing client for account %s", accountName); + if (client.getAccount() != null && + client.getAccount().getCredentials() != null && + (client.getAccount().getCredentials().getAuthToken() == null || client.getAccount().getCredentials().getAuthToken().isEmpty()) + ) { + Timber.i("Client " + client.getAccount().getName() + " needs to refresh credentials"); + + //the next two lines are a hack because okHttpclient is used as a singleton instead of being an + //injected instance that can be deleted when required + client.clearCookies(); + client.clearCredentials(); + + client.setAccount(account); + + account.loadCredentials(context); + client.setCredentials(account.getCredentials()); + + Timber.i("Client " + account.getName() + " with credentials size" + client.getAccount().getCredentials().getAuthToken().length()); + } reusingKnown = true; } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/accounts/AccountUtils.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/accounts/AccountUtils.java index 535104df..224d647b 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/accounts/AccountUtils.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/accounts/AccountUtils.java @@ -112,6 +112,7 @@ public class AccountUtils { String username = AccountUtils.getUsernameForAccount(account); if (isOauth2) { + Timber.i("Trying to retrieve credentials for oAuth account" + account.name); String accessToken = am.blockingGetAuthToken( account, AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java index 8978a556..475c6c3f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java @@ -40,6 +40,7 @@ public class HttpConstants { public static final String IF_MATCH_HEADER = "If-Match"; public static final String IF_NONE_MATCH_HEADER = "If-None-Match"; public static final String CONTENT_TYPE_HEADER = "Content-Type"; + public static final String ACCEPT_LANGUAGE_HEADER = "Accept-Language"; public static final String CONTENT_LENGTH_HEADER = "Content-Length"; public static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length"; public static final String OC_X_OC_MTIME_HEADER = "X-OC-Mtime"; diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/LogInterceptor.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/LogInterceptor.kt index 0ca90a47..eea604d9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/LogInterceptor.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/LogInterceptor.kt @@ -51,7 +51,7 @@ class LogInterceptor : Interceptor { val request = chain.request().also { val requestId = it.headers[OC_X_REQUEST_ID] - logHttp(REQUEST, INFO, requestId, "Type: ${it.method} URL: ${it.url}") + logHttp(REQUEST, INFO, requestId, "Method: ${it.method} URL: ${it.url}") logHeaders(requestId, it.headers, REQUEST) logRequestBody(requestId, it.body) } @@ -64,7 +64,7 @@ class LogInterceptor : Interceptor { RESPONSE, INFO, requestId, - "Code: ${it.code} Message: ${it.message} IsSuccessful: ${it.isSuccessful}" + "Method: ${request.method} URL: ${request.url} Code: ${it.code} Message: ${it.message}" ) logHeaders(requestId, it.headers, RESPONSE) logResponseBody(requestId, it.body) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/DavUtils.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/DavUtils.kt index a48626d3..ce772609 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/DavUtils.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/DavUtils.kt @@ -24,13 +24,36 @@ package com.owncloud.android.lib.common.http.methods.webdav import at.bitfire.dav4jvm.Property -import at.bitfire.dav4jvm.PropertyUtils.getAllPropSet import at.bitfire.dav4jvm.PropertyUtils.getQuotaPropset +import at.bitfire.dav4jvm.property.CreationDate +import at.bitfire.dav4jvm.property.DisplayName +import at.bitfire.dav4jvm.property.GetContentLength +import at.bitfire.dav4jvm.property.GetContentType +import at.bitfire.dav4jvm.property.GetETag +import at.bitfire.dav4jvm.property.GetLastModified +import at.bitfire.dav4jvm.property.OCId +import at.bitfire.dav4jvm.property.OCPermissions +import at.bitfire.dav4jvm.property.OCPrivatelink +import at.bitfire.dav4jvm.property.OCSize +import at.bitfire.dav4jvm.property.ResourceType import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes object DavUtils { - @JvmStatic val allPropset: Array - get() = getAllPropSet().plus(OCShareTypes.NAME) + @JvmStatic val allPropSet: Array + get() = arrayOf( + DisplayName.NAME, + GetContentType.NAME, + ResourceType.NAME, + GetContentLength.NAME, + GetLastModified.NAME, + CreationDate.NAME, + GetETag.NAME, + OCPermissions.NAME, + OCId.NAME, + OCSize.NAME, + OCPrivatelink.NAME, + OCShareTypes.NAME, + ) val quotaPropSet: Array get() = getQuotaPropset() diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java index 5d849b08..b56db410 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java @@ -46,6 +46,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.net.MalformedURLException; +import java.net.ProtocolException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; @@ -166,7 +167,10 @@ public class RemoteOperationResult } else if (e instanceof FileNotFoundException) { mCode = ResultCode.LOCAL_FILE_NOT_FOUND; - } else { + } else if (e instanceof ProtocolException) { + mCode = ResultCode.NETWORK_ERROR; + } + else { mCode = ResultCode.UNKNOWN_ERROR; } } @@ -589,5 +593,6 @@ public class RemoteOperationResult SPECIFIC_METHOD_NOT_ALLOWED, SPECIFIC_BAD_REQUEST, TOO_EARLY, + NETWORK_ERROR, } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/CreateRemoteFileWithAppProviderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/CreateRemoteFileWithAppProviderOperation.kt new file mode 100644 index 00000000..e4f85a32 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/CreateRemoteFileWithAppProviderOperation.kt @@ -0,0 +1,108 @@ +/* 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.resources.appregistry + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.nonwebdav.PostMethod +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.squareup.moshi.Json +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonClass +import com.squareup.moshi.Moshi +import okhttp3.FormBody +import okhttp3.RequestBody +import timber.log.Timber +import java.net.URL +import java.util.concurrent.TimeUnit + +class CreateRemoteFileWithAppProviderOperation( + private val createFileWithAppProviderEndpoint: String, + private val parentContainerId: String, + private val filename: String, +) : RemoteOperation() { + + override fun run(client: OwnCloudClient): RemoteOperationResult { + return try { + + val createFileWithAppProviderRequestBody = CreateFileWithAppProviderParams(parentContainerId, filename) + .toRequestBody() + + val stringUrl = client.baseUri.toString() + WebdavUtils.encodePath(createFileWithAppProviderEndpoint) + + val postMethod = PostMethod(URL(stringUrl), createFileWithAppProviderRequestBody).apply { + setReadTimeout(TIMEOUT, TimeUnit.MILLISECONDS) + setConnectionTimeout(TIMEOUT, TimeUnit.MILLISECONDS) + } + + val status = client.executeHttpMethod(postMethod) + Timber.d("Create file $filename with app provider in folder with ID $parentContainerId - $status${if (!isSuccess(status)) "(FAIL)" else ""}") + + if (isSuccess(status)) RemoteOperationResult(ResultCode.OK).apply { + val moshi = Moshi.Builder().build() + val adapter: JsonAdapter = moshi.adapter(CreateFileWithAppProviderResponse::class.java) + + data = postMethod.getResponseBodyAsString()?.let { adapter.fromJson(it)!!.fileId } + } + else RemoteOperationResult(postMethod).apply { data = "" } + + } catch (e: Exception) { + val result = RemoteOperationResult(e) + Timber.e(e, "Create file $filename with app provider in folder with ID $parentContainerId failed") + result + } + } + + private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK + + data class CreateFileWithAppProviderParams( + val parentContainerId: String, + val filename: String, + ) { + fun toRequestBody(): RequestBody = + FormBody.Builder() + .add(PARAM_PARENT_CONTAINER_ID, parentContainerId) + .add(PARAM_FILENAME, filename) + .build() + + companion object { + const val PARAM_PARENT_CONTAINER_ID = "parent_container_id" + const val PARAM_FILENAME = "filename" + } + } + + @JsonClass(generateAdapter = true) + data class CreateFileWithAppProviderResponse( + @Json(name = "file_id") + val fileId: String, + ) + + companion object { + private const val TIMEOUT: Long = 5_000 + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/GetRemoteAppRegistryOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/GetRemoteAppRegistryOperation.kt new file mode 100644 index 00000000..60367ff0 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/GetRemoteAppRegistryOperation.kt @@ -0,0 +1,82 @@ +/* ownCloud Android Library is available under MIT license + * @author Abel García de Prada + * + * 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.resources.appregistry + +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.common.operations.RemoteOperationResult.ResultCode.OK +import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import timber.log.Timber +import java.net.URL + +class GetRemoteAppRegistryOperation : RemoteOperation() { + + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + + try { + val uriBuilder = client.baseUri.buildUpon().apply { + appendEncodedPath(APP_REGISTRY_ENDPOINT) + } + val getMethod = GetMethod(URL(uriBuilder.build().toString())) + val status = client.executeHttpMethod(getMethod) + + val response = getMethod.getResponseBodyAsString() + + if (status == HttpConstants.HTTP_OK) { + Timber.d("Successful response $response") + + // Parse the response + val moshi: Moshi = Moshi.Builder().build() + val adapter: JsonAdapter = moshi.adapter(AppRegistryResponse::class.java) + val appRegistryResponse: AppRegistryResponse = response?.let { adapter.fromJson(it) } ?: AppRegistryResponse(value = emptyList()) + + result = RemoteOperationResult(OK) + result.data = appRegistryResponse + + Timber.d("Get AppRegistry completed and parsed to ${result.data}") + } else { + result = RemoteOperationResult(getMethod) + Timber.e("Failed response while getting app registry from the server status code: $status; response message: $response") + } + + } catch (e: Exception) { + result = RemoteOperationResult(e) + Timber.e(e, "Exception while getting app registry") + } + + return result + } + + companion object { + private const val APP_REGISTRY_ENDPOINT = "app/list" + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetUrlToOpenInWebRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/GetUrlToOpenInWebRemoteOperation.kt similarity index 83% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetUrlToOpenInWebRemoteOperation.kt rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/GetUrlToOpenInWebRemoteOperation.kt index b36cb760..84b32514 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetUrlToOpenInWebRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/GetUrlToOpenInWebRemoteOperation.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 @@ -21,7 +21,8 @@ * THE SOFTWARE. * */ -package com.owncloud.android.lib.resources.files + +package com.owncloud.android.lib.resources.appregistry import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.http.HttpConstants @@ -30,7 +31,6 @@ import com.owncloud.android.lib.common.network.WebdavUtils import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode -import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation.OpenInWebParams.Companion.PARAM_FILE_ID import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonClass import com.squareup.moshi.Moshi @@ -41,16 +41,18 @@ import java.net.URL import java.util.concurrent.TimeUnit class GetUrlToOpenInWebRemoteOperation( - val openWithWebEndpoint: String, - val fileId: String, + private val openWithWebEndpoint: String, + private val fileId: String, + private val appName: String, ) : RemoteOperation() { override fun run(client: OwnCloudClient): RemoteOperationResult { return try { - val openInWebRequestBody = OpenInWebParams(fileId).toRequestBody() + val openInWebRequestBody = OpenInWebParams(fileId, appName).toRequestBody() - val stringUrl = client.baseUri.toString() + WebdavUtils.encodePath(openWithWebEndpoint) + "?$PARAM_FILE_ID=$fileId" + val stringUrl = + client.baseUri.toString() + WebdavUtils.encodePath(openWithWebEndpoint) val postMethod = PostMethod(URL(stringUrl), openInWebRequestBody).apply { setReadTimeout(TIMEOUT, TimeUnit.MILLISECONDS) @@ -75,14 +77,21 @@ class GetUrlToOpenInWebRemoteOperation( } } - private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS + private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK - data class OpenInWebParams(val fileId: String) { + data class OpenInWebParams( + val fileId: String, + val appName: String, + ) { fun toRequestBody(): RequestBody = - FormBody.Builder().build() + FormBody.Builder() + .add(PARAM_FILE_ID, fileId) + .add(PARAM_APP_NAME, appName) + .build() companion object { const val PARAM_FILE_ID = "file_id" + const val PARAM_APP_NAME = "app_name" } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/responses/AppRegistryResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/responses/AppRegistryResponse.kt new file mode 100644 index 00000000..f3f247d9 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/responses/AppRegistryResponse.kt @@ -0,0 +1,56 @@ +/* ownCloud Android Library is available under MIT license + * @author Abel García de Prada + * + * 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.resources.appregistry.responses + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class AppRegistryResponse( + @Json(name = "mime-types") + val value: List +) + +@JsonClass(generateAdapter = true) +data class AppRegistryMimeTypeResponse( + @Json(name = "mime_type") val mimeType: String, + val ext: String? = null, + @Json(name = "app_providers") + val appProviders: List, + val name: String? = null, + val icon: String? = null, + val description: String? = null, + @Json(name = "allow_creation") + val allowCreation: Boolean? = null, + @Json(name = "default_application") + val defaultApplication: String? = null +) + +@JsonClass(generateAdapter = true) +data class AppRegistryProviderResponse( + val name: String, + val icon: String, +) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/services/AppRegistryService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/services/AppRegistryService.kt new file mode 100644 index 00000000..50145892 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/services/AppRegistryService.kt @@ -0,0 +1,44 @@ +/* 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.resources.appregistry.services + +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.Service +import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse + +interface AppRegistryService : Service { + fun getAppRegistry(): RemoteOperationResult + + fun getUrlToOpenInWeb( + openWebEndpoint: String, + fileId: String, + appName: String, + ): RemoteOperationResult + + fun createFileWithAppProvider( + createFileWithAppProviderEndpoint: String, + parentContainerId: String, + filename: String, + ): RemoteOperationResult +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/services/OCAppRegistryService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/services/OCAppRegistryService.kt new file mode 100644 index 00000000..e07ac100 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/services/OCAppRegistryService.kt @@ -0,0 +1,54 @@ +/* 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.resources.appregistry.services + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.appregistry.CreateRemoteFileWithAppProviderOperation +import com.owncloud.android.lib.resources.appregistry.GetRemoteAppRegistryOperation +import com.owncloud.android.lib.resources.appregistry.GetUrlToOpenInWebRemoteOperation +import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse + +class OCAppRegistryService(override val client: OwnCloudClient) : AppRegistryService { + override fun getAppRegistry(): RemoteOperationResult = + GetRemoteAppRegistryOperation().execute(client) + + override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String, appName: String): RemoteOperationResult = + GetUrlToOpenInWebRemoteOperation( + openWithWebEndpoint = openWebEndpoint, + fileId = fileId, + appName = appName + ).execute(client) + + override fun createFileWithAppProvider( + createFileWithAppProviderEndpoint: String, + parentContainerId: String, + filename: String + ): RemoteOperationResult = + CreateRemoteFileWithAppProviderOperation( + createFileWithAppProviderEndpoint = createFileWithAppProviderEndpoint, + parentContainerId = parentContainerId, + filename = filename, + ).execute(client) +} 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..da9b685f 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 @@ -25,7 +25,7 @@ package com.owncloud.android.lib.resources.files import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.http.HttpConstants -import com.owncloud.android.lib.common.http.methods.webdav.DavUtils.allPropset +import com.owncloud.android.lib.common.http.methods.webdav.DavUtils.allPropSet import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod import com.owncloud.android.lib.common.network.WebdavUtils import com.owncloud.android.lib.common.operations.RemoteOperation @@ -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) - val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropset).apply { + 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/GetBaseUrlRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt index 904a9bea..65a8faf9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt @@ -44,7 +44,7 @@ class GetBaseUrlRemoteOperation : RemoteOperation() { return try { val stringUrl = client.baseFilesWebDavUri.toString() - val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropset).apply { + val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropSet).apply { setReadTimeout(TIMEOUT, TimeUnit.SECONDS) setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS) } 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..787fb751 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,9 +60,9 @@ 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 + propertiesToRequest = DavUtils.allPropSet ).apply { setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS) setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS) @@ -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,9 +62,9 @@ class ReadRemoteFolderOperation( PropertyRegistry.register(OCShareTypes.Factory()) val propfindMethod = PropfindMethod( - URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)), + getFinalWebDavUrl(), DavConstants.DEPTH_1, - DavUtils.allPropset + DavUtils.allPropSet ) val status = client.executeHttpMethod(propfindMethod) @@ -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..16ec6748 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 @@ -38,8 +39,6 @@ import at.bitfire.dav4jvm.property.OCId import at.bitfire.dav4jvm.property.OCPermissions import at.bitfire.dav4jvm.property.OCPrivatelink import at.bitfire.dav4jvm.property.OCSize -import at.bitfire.dav4jvm.property.QuotaAvailableBytes -import at.bitfire.dav4jvm.property.QuotaUsedBytes import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes @@ -50,7 +49,6 @@ import kotlinx.parcelize.Parcelize import okhttp3.HttpUrl import timber.log.Timber import java.io.File -import java.math.BigDecimal /** * Contains the data of a Remote File from a WebDavEntry @@ -72,8 +70,6 @@ data class RemoteFile( var permissions: String? = null, var remoteId: String? = null, var size: Long = 0, - var quotaUsedBytes: BigDecimal? = null, - var quotaAvailableBytes: BigDecimal? = null, var privateLink: String? = null, var owner: String, var sharedByLink: Boolean = false, @@ -100,8 +96,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) @@ -131,12 +132,6 @@ data class RemoteFile( is OCSize -> { remoteFile.size = property.size } - is QuotaUsedBytes -> { - remoteFile.quotaUsedBytes = BigDecimal.valueOf(property.quotaUsedBytes) - } - is QuotaAvailableBytes -> { - remoteFile.quotaAvailableBytes = BigDecimal.valueOf(property.quotaAvailableBytes) - } is OCPrivatelink -> { remoteFile.privateLink = property.link } @@ -164,15 +159,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..b524dc3f 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 @@ -28,22 +28,24 @@ import com.owncloud.android.lib.resources.Service import com.owncloud.android.lib.resources.files.RemoteFile interface FileService : Service { - fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult - 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 +56,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 +79,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..7b86a4fa 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 @@ -29,7 +29,6 @@ import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperatio import com.owncloud.android.lib.resources.files.CopyRemoteFileOperation import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation -import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation @@ -39,37 +38,41 @@ import com.owncloud.android.lib.resources.files.RenameRemoteFileOperation import com.owncloud.android.lib.resources.files.services.FileService 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 = - GetUrlToOpenInWebRemoteOperation(openWithWebEndpoint = openWebEndpoint, fileId = fileId).execute(client) - 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 +87,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/shares/CreateRemoteShareOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.kt index ed2b7417..fed12f83 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.kt @@ -89,8 +89,6 @@ class CreateRemoteShareOperation( var expirationDateInMillis: Long = INIT_EXPIRATION_DATE_IN_MILLIS // Expiration date to set for the public link - var publicUpload: Boolean = false // Upload permissions for the public link (only folders) - var retrieveShareDetails = false // To retrieve more info about the just created share private fun buildRequestUri(baseUri: Uri) = @@ -99,7 +97,7 @@ class CreateRemoteShareOperation( .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) .build() - private fun parseResponse(response: String): ShareResponse? { + private fun parseResponse(response: String): ShareResponse { val moshi = Moshi.Builder().build() val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareItem::class.java) val adapter: JsonAdapter> = moshi.adapter(commonOcsType) @@ -155,12 +153,10 @@ class CreateRemoteShareOperation( formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate) } - if (publicUpload) { - formBodyBuilder.add(PARAM_PUBLIC_UPLOAD, publicUpload.toString()) - } if (password.isNotEmpty()) { formBodyBuilder.add(PARAM_PASSWORD, password) } + if (RemoteShare.DEFAULT_PERMISSION != permissions) { formBodyBuilder.add(PARAM_PERMISSIONS, permissions.toString()) } @@ -207,7 +203,6 @@ class CreateRemoteShareOperation( private const val PARAM_SHARE_TYPE = "shareType" private const val PARAM_SHARE_WITH = "shareWith" private const val PARAM_PASSWORD = "password" - private const val PARAM_PUBLIC_UPLOAD = "publicUpload" private const val PARAM_PERMISSIONS = "permissions" //Arguments - constant values diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt index 304ead67..54280604 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt @@ -36,13 +36,7 @@ import com.owncloud.android.lib.common.http.HttpConstants.VALUE_FORMAT import com.owncloud.android.lib.common.http.methods.nonwebdav.DeleteMethod import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.CommonOcsResponse -import com.owncloud.android.lib.resources.shares.responses.ShareItem -import com.squareup.moshi.JsonAdapter -import com.squareup.moshi.Moshi -import com.squareup.moshi.Types import timber.log.Timber -import java.lang.reflect.Type import java.net.URL /** @@ -52,14 +46,10 @@ import java.net.URL * @author David A. Velasco * @author David González Verdugo * @author Fernando Sanz Velasco - */ - -/** - * Constructor * * @param remoteShareId Share ID */ -class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation() { +class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation() { private fun buildRequestUri(baseUri: Uri) = baseUri.buildUpon() @@ -68,24 +58,12 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) .build() - private fun parseResponse(response: String): ShareResponse? { - val moshi = Moshi.Builder().build() - val listOfShareItemType: Type = Types.newParameterizedType(List::class.java, ShareItem::class.java) - val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, listOfShareItemType) - val adapter: JsonAdapter>> = moshi.adapter(commonOcsType) - return adapter.fromJson(response)?.ocs?.data?.let { listOfShareItems -> - ShareResponse(listOfShareItems.map { shareItem -> - shareItem.toRemoteShare() - }) - } - } - private fun onResultUnsuccessful( method: DeleteMethod, response: String?, status: Int - ): RemoteOperationResult { - Timber.e("Failed response while unshare link ") + ): RemoteOperationResult { + Timber.e("Failed response while removing share ") if (response != null) { Timber.e("*** status code: $status; response message: $response") } else { @@ -94,17 +72,14 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper return RemoteOperationResult(method) } - private fun onRequestSuccessful(response: String?): RemoteOperationResult { - val result = RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + private fun onRequestSuccessful(response: String?): RemoteOperationResult { + val result = RemoteOperationResult(RemoteOperationResult.ResultCode.OK) Timber.d("Successful response: $response") - result.data = parseResponse(response!!) Timber.d("*** Unshare link completed ") return result } - override fun run(client: OwnCloudClient): RemoteOperationResult { - - + override fun run(client: OwnCloudClient): RemoteOperationResult { val requestUri = buildRequestUri(client.baseUri) val deleteMethod = DeleteMethod(URL(requestUri.toString())).apply { @@ -129,7 +104,7 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK companion object { - //OCS Route + // OCS Route private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/shares" } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt index a6cc71e3..b9f814a2 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt @@ -103,13 +103,6 @@ class UpdateRemoteShareOperation */ var permissions: Int = DEFAULT_PERMISSION - /** - * Enable upload permissions to update in Share resource. - * - * Null results in no update applied to the upload permission. - */ - var publicUpload: Boolean? = null - var retrieveShareDetails = false // To retrieve more info about the just updated share private fun buildRequestUri(baseUri: Uri) = @@ -119,7 +112,7 @@ class UpdateRemoteShareOperation .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) .build() - private fun parseResponse(response: String): ShareResponse? { + private fun parseResponse(response: String): ShareResponse { val moshi = Moshi.Builder().build() val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareItem::class.java) val adapter: JsonAdapter> = moshi.adapter(commonOcsType) @@ -181,10 +174,6 @@ class UpdateRemoteShareOperation formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate) } // else, ignore - no update - if (publicUpload != null) { - formBodyBuilder.add(PARAM_PUBLIC_UPLOAD, publicUpload.toString()) - } - // IMPORTANT: permissions parameter needs to be updated after mPublicUpload parameter, // otherwise they would be set always as 1 (READ) in the server when mPublicUpload was updated if (permissions > DEFAULT_PERMISSION) { @@ -233,7 +222,6 @@ class UpdateRemoteShareOperation private const val PARAM_PASSWORD = "password" private const val PARAM_EXPIRATION_DATE = "expireDate" private const val PARAM_PERMISSIONS = "permissions" - private const val PARAM_PUBLIC_UPLOAD = "publicUpload" //Arguments - constant values private const val FORMAT_EXPIRATION_DATE = "yyyy-MM-dd" diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt index 9774ca38..53458c59 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt @@ -46,7 +46,6 @@ interface ShareService : Service { name: String, password: String, expirationDate: Long, - publicUpload: Boolean ): RemoteOperationResult fun updateShare( @@ -55,8 +54,7 @@ interface ShareService : Service { password: String?, expirationDate: Long, permissions: Int, - publicUpload: Boolean ): RemoteOperationResult - fun deleteShare(remoteId: String): RemoteOperationResult + fun deleteShare(remoteId: String): RemoteOperationResult } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt index f8bcd1ea..96381b6a 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt @@ -36,8 +36,7 @@ import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.lib.resources.shares.UpdateRemoteShareOperation import com.owncloud.android.lib.resources.shares.services.ShareService -class OCShareService(override val client: OwnCloudClient) : - ShareService { +class OCShareService(override val client: OwnCloudClient) : ShareService { override fun getShares( remoteFilePath: String, reshares: Boolean, @@ -56,7 +55,6 @@ class OCShareService(override val client: OwnCloudClient) : name: String, password: String, expirationDate: Long, - publicUpload: Boolean ): RemoteOperationResult = CreateRemoteShareOperation( remoteFilePath, @@ -67,7 +65,6 @@ class OCShareService(override val client: OwnCloudClient) : this.name = name this.password = password this.expirationDateInMillis = expirationDate - this.publicUpload = publicUpload this.retrieveShareDetails = true }.execute(client) @@ -77,7 +74,6 @@ class OCShareService(override val client: OwnCloudClient) : password: String?, expirationDate: Long, permissions: Int, - publicUpload: Boolean ): RemoteOperationResult = UpdateRemoteShareOperation( remoteId @@ -86,11 +82,10 @@ class OCShareService(override val client: OwnCloudClient) : this.password = password this.expirationDateInMillis = expirationDate this.permissions = permissions - this.publicUpload = publicUpload this.retrieveShareDetails = true }.execute(client) - override fun deleteShare(remoteId: String): RemoteOperationResult = + override fun deleteShare(remoteId: String): RemoteOperationResult = RemoveRemoteShareOperation( remoteId ).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..213e8871 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt @@ -0,0 +1,98 @@ +/* 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 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 DeleteResponse( + val state: String, +) + +@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/status/services/implementation/OCCapabilityService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCCapabilityService.kt index e211475f..01c9c628 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCCapabilityService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCCapabilityService.kt @@ -1,8 +1,6 @@ /* ownCloud Android Library is available under MIT license * Copyright (C) 2022 ownCloud GmbH. * - * @author David González Verdugo - * * 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 @@ -32,8 +30,7 @@ import com.owncloud.android.lib.resources.status.GetRemoteCapabilitiesOperation import com.owncloud.android.lib.resources.status.RemoteCapability import com.owncloud.android.lib.resources.status.services.CapabilityService -class OCCapabilityService(override val client: OwnCloudClient) : - CapabilityService { +class OCCapabilityService(override val client: OwnCloudClient) : CapabilityService { override fun getCapabilities(): RemoteOperationResult = GetRemoteCapabilitiesOperation().execute(client) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCServerInfoService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCServerInfoService.kt index d2c891f0..0997f712 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCServerInfoService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCServerInfoService.kt @@ -1,8 +1,6 @@ /* ownCloud Android Library is available under MIT license * Copyright (C) 2022 ownCloud GmbH. * - * @author Abel García de Prada - * * 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 @@ -26,7 +24,6 @@ package com.owncloud.android.lib.resources.status.services.implementation - import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation @@ -39,16 +36,16 @@ class OCServerInfoService : ServerInfoService { override fun checkPathExistence( path: String, isUserLoggedIn: Boolean, - client: OwnCloudClient + client: OwnCloudClient, ): RemoteOperationResult = CheckPathExistenceRemoteOperation( remotePath = path, - isUserLoggedIn = true + isUserLoggedIn = true, ).execute(client) override fun getRemoteStatus( path: String, - client: OwnCloudClient + client: OwnCloudClient, ): RemoteOperationResult = GetRemoteStatusOperation().execute(client) } 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..0a19b5e8 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,32 +61,31 @@ 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 } ?: listOf() + return operationResult } - override fun run(client: OwnCloudClient): RemoteOperationResult { + override fun run(client: OwnCloudClient): RemoteOperationResult> { val requestUri = buildRequestUri() val getMethod = GetMethod(URL(requestUri.toString())) + + // First iteration won't follow redirections. + getMethod.followRedirects = false + return try { val status = client.executeHttpMethod(getMethod) val response = getMethod.getResponseBodyAsString()!! @@ -97,8 +96,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 94% 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..aeb6af8f 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 + 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 87% 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..4b275cd7 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,34 +8,34 @@ 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 fun `check rel in too much information - ok`() { val response = loadResponses(TOO_MUCH_INFORMATION_JSON)!! - Assert.assertEquals("https://gast.somedomain.de", response.links[0].href) - Assert.assertEquals("http://webfinger.owncloud/rel/server-instance", response.links[0].rel) + Assert.assertEquals("https://gast.somedomain.de", response.links!![0].href) + Assert.assertEquals("http://webfinger.owncloud/rel/server-instance", response.links!![0].rel) } @Test(expected = JsonDataException::class) fun `check key value pairs - ko - no href key`() { val response = loadResponses(BROKEN_JSON)!! - Assert.assertEquals("https://gast.somedomain.de", response.links[0].href) + Assert.assertEquals("https://gast.somedomain.de", response.links!![0].href) } @Test(expected = JsonDataException::class) fun `check key value pairs - ko - no rel key`() { val response = loadResponses(BROKEN_JSON)!! - Assert.assertEquals("https://gast.somedomain.de", response.links[0].href) + Assert.assertEquals("https://gast.somedomain.de", response.links!![0].href) } companion object {