mirror of
				https://github.com/owncloud/android-library.git
				synced 2025-10-31 02:17:41 +00:00 
			
		
		
		
	
						commit
						d7260426d5
					
				
							
								
								
									
										15
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								build.gradle
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | |||||||
| buildscript { | buildscript { | ||||||
|     ext { |     ext { | ||||||
|         kotlinVersion = '1.6.21' |         orgJetbrainsKotlin = '1.8.10' | ||||||
|         moshiVersion = "1.13.0" |         comSquareupMoshi = '1.14.0' | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     repositories { |     repositories { | ||||||
| @ -10,12 +10,16 @@ buildscript { | |||||||
|         maven { url "https://plugins.gradle.org/m2/" } |         maven { url "https://plugins.gradle.org/m2/" } | ||||||
|     } |     } | ||||||
|     dependencies { |     dependencies { | ||||||
|         classpath "org.jlleitschuh.gradle:ktlint-gradle:10.3.0" |         classpath "org.jlleitschuh.gradle:ktlint-gradle:11.1.0" | ||||||
|         classpath 'com.android.tools.build:gradle:7.1.3' |         classpath 'com.android.tools.build:gradle:7.4.2' | ||||||
|         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" |         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$orgJetbrainsKotlin" | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | plugins { | ||||||
|  |     id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false | ||||||
|  | } | ||||||
|  | 
 | ||||||
| allprojects { | allprojects { | ||||||
|     repositories { |     repositories { | ||||||
|         google() |         google() | ||||||
| @ -26,4 +30,5 @@ allprojects { | |||||||
| 
 | 
 | ||||||
| subprojects { | subprojects { | ||||||
|     apply plugin: "org.jlleitschuh.gradle.ktlint" |     apply plugin: "org.jlleitschuh.gradle.ktlint" | ||||||
|  |     apply plugin: "com.google.devtools.ksp" | ||||||
| } | } | ||||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +1,5 @@ | |||||||
| distributionBase=GRADLE_USER_HOME | distributionBase=GRADLE_USER_HOME | ||||||
| distributionPath=wrapper/dists | 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 | zipStoreBase=GRADLE_USER_HOME | ||||||
| zipStorePath=wrapper/dists | zipStorePath=wrapper/dists | ||||||
|  | |||||||
| @ -1,32 +1,32 @@ | |||||||
| apply plugin: 'com.android.library' | apply plugin: 'com.android.library' | ||||||
| apply plugin: 'kotlin-android' | apply plugin: 'kotlin-android' | ||||||
| apply plugin: 'kotlin-kapt' | apply plugin: 'com.google.devtools.ksp' | ||||||
| apply plugin: 'kotlin-parcelize' | apply plugin: 'kotlin-parcelize' | ||||||
| 
 | 
 | ||||||
| dependencies { | dependencies { | ||||||
|     api 'com.squareup.okhttp3:okhttp:4.6.0' |     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.gitlab.ownclouders:dav4android:oc_support_2.1.5' | ||||||
|     api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2' |     api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2' | ||||||
| 
 | 
 | ||||||
|     // Moshi |     // Moshi | ||||||
|     implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") { |     implementation("com.squareup.moshi:moshi-kotlin:$comSquareupMoshi") { | ||||||
|         exclude module: "kotlin-reflect" |         exclude module: "kotlin-reflect" | ||||||
|     } |     } | ||||||
|     implementation 'org.apache.commons:commons-lang3:3.12.0' |     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 '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' |     debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.6.0' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| android { | android { | ||||||
|     compileSdkVersion 31 |     compileSdkVersion 33 | ||||||
| 
 | 
 | ||||||
|     defaultConfig { |     defaultConfig { | ||||||
|         minSdkVersion 21 |         minSdkVersion 21 | ||||||
|         targetSdkVersion 31 |         targetSdkVersion 33 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     lint { |     lint { | ||||||
| @ -39,4 +39,5 @@ android { | |||||||
|             includeAndroidResources = true |             includeAndroidResources = true | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     namespace 'com.owncloud.android.lib' | ||||||
| } | } | ||||||
|  | |||||||
| @ -23,8 +23,7 @@ | |||||||
| 
 | 
 | ||||||
|  --> |  --> | ||||||
| 
 | 
 | ||||||
| <manifest package="com.owncloud.android.lib" | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||||||
|     xmlns:android="http://schemas.android.com/apk/res/android"> |  | ||||||
| 
 | 
 | ||||||
|     <!-- USE_CREDENTIALS, MANAGE_ACCOUNTS and AUTHENTICATE_ACCOUNTS are needed for API < 23. |     <!-- USE_CREDENTIALS, MANAGE_ACCOUNTS and AUTHENTICATE_ACCOUNTS are needed for API < 23. | ||||||
|         In API >= 23 the do not exist anymore --> |         In API >= 23 the do not exist anymore --> | ||||||
|  | |||||||
| @ -37,7 +37,6 @@ import com.owncloud.android.lib.resources.status.RemoteServerInfo | |||||||
| import org.apache.commons.lang3.exception.ExceptionUtils | import org.apache.commons.lang3.exception.ExceptionUtils | ||||||
| import timber.log.Timber | import timber.log.Timber | ||||||
| import java.io.IOException | import java.io.IOException | ||||||
| import java.lang.Exception |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * ConnectionValidator |  * ConnectionValidator | ||||||
| @ -46,7 +45,7 @@ import java.lang.Exception | |||||||
|  */ |  */ | ||||||
| class ConnectionValidator( | class ConnectionValidator( | ||||||
|     val context: Context, |     val context: Context, | ||||||
|     val clearCookiesOnValidation: Boolean |     private val clearCookiesOnValidation: Boolean | ||||||
| ) { | ) { | ||||||
|     fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager, context: Context): Boolean { |     fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager, context: Context): Boolean { | ||||||
|         try { |         try { | ||||||
| @ -61,12 +60,12 @@ class ConnectionValidator( | |||||||
|             client.account = baseClient.account |             client.account = baseClient.account | ||||||
|             client.credentials = baseClient.credentials |             client.credentials = baseClient.credentials | ||||||
|             while (validationRetryCount < VALIDATION_RETRY_COUNT) { |             while (validationRetryCount < VALIDATION_RETRY_COUNT) { | ||||||
|                 Timber.d("validationRetryCout %d", validationRetryCount) |                 Timber.d("validationRetryCount %d", validationRetryCount) | ||||||
|                 var successCounter = 0 |                 var successCounter = 0 | ||||||
|                 var failCounter = 0 |                 var failCounter = 0 | ||||||
| 
 | 
 | ||||||
|                 client.setFollowRedirects(true) |                 client.setFollowRedirects(true) | ||||||
|                 if (isOnwCloudStatusOk(client)) { |                 if (isOwnCloudStatusOk(client)) { | ||||||
|                     successCounter++ |                     successCounter++ | ||||||
|                 } else { |                 } else { | ||||||
|                     failCounter++ |                     failCounter++ | ||||||
| @ -103,7 +102,7 @@ class ConnectionValidator( | |||||||
|         return false |         return false | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun isOnwCloudStatusOk(client: OwnCloudClient): Boolean { |     private fun isOwnCloudStatusOk(client: OwnCloudClient): Boolean { | ||||||
|         val reply = getOwnCloudStatus(client) |         val reply = getOwnCloudStatus(client) | ||||||
|         // dont check status code. It currently relais on the broken redirect code of the owncloud 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 |         // TODO: Use okhttp redirect and add this check again | ||||||
| @ -138,6 +137,12 @@ class ConnectionValidator( | |||||||
|         // test if have all the needed to effectively invalidate ... |         // test if have all the needed to effectively invalidate ... | ||||||
|         shouldInvalidateAccountCredentials = |         shouldInvalidateAccountCredentials = | ||||||
|             shouldInvalidateAccountCredentials and (account.savedAccount != null) |             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 |         return shouldInvalidateAccountCredentials | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -150,6 +155,7 @@ class ConnectionValidator( | |||||||
|      * |      * | ||||||
|      */ |      */ | ||||||
|     private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) { |     private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) { | ||||||
|  |         Timber.i("Invalidating account credentials for account $account") | ||||||
|         val am = AccountManager.get(context) |         val am = AccountManager.get(context) | ||||||
|         am.invalidateAuthToken( |         am.invalidateAuthToken( | ||||||
|             account.savedAccount.type, |             account.savedAccount.type, | ||||||
| @ -167,8 +173,6 @@ class ConnectionValidator( | |||||||
|      * |      * | ||||||
|      * Refresh current credentials if possible, and marks a retry. |      * Refresh current credentials if possible, and marks a retry. | ||||||
|      * |      * | ||||||
|      * @param status |  | ||||||
|      * @param repeatCounter |  | ||||||
|      * @return |      * @return | ||||||
|      */ |      */ | ||||||
|     private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean { |     private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean { | ||||||
| @ -181,6 +185,7 @@ class ConnectionValidator( | |||||||
|             if (credentials.authTokenCanBeRefreshed()) { |             if (credentials.authTokenCanBeRefreshed()) { | ||||||
|                 try { |                 try { | ||||||
|                     // This command does the actual refresh |                     // This command does the actual refresh | ||||||
|  |                     Timber.i("Trying to refresh auth token for account $account") | ||||||
|                     account.loadCredentials(context) |                     account.loadCredentials(context) | ||||||
|                     // if mAccount.getCredentials().length() == 0 --> refresh failed |                     // if mAccount.getCredentials().length() == 0 --> refresh failed | ||||||
|                     client.credentials = account.credentials |                     client.credentials = account.credentials | ||||||
| @ -201,6 +206,7 @@ class ConnectionValidator( | |||||||
|                 if (!credentialsWereRefreshed) { |                 if (!credentialsWereRefreshed) { | ||||||
|                     // if credentials are not refreshed, client must be removed |                     // if credentials are not refreshed, client must be removed | ||||||
|                     // from the OwnCloudClientManager to prevent it is reused once and again |                     // 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) |                     singleSessionManager.removeClientFor(account) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @ -210,6 +216,6 @@ class ConnectionValidator( | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     companion object { |     companion object { | ||||||
|         private val VALIDATION_RETRY_COUNT = 3 |         private const val VALIDATION_RETRY_COUNT = 3 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -43,6 +43,7 @@ import timber.log.Timber; | |||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| import java.util.List; | 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.AUTHORIZATION_HEADER; | ||||||
| import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY; | 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); |             Timber.d("Executing in request with id %s", requestId); | ||||||
|             method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId); |             method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId); | ||||||
|             method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent()); |             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); |             method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY); | ||||||
|             if (mCredentials.getHeaderAuth() != null && !mCredentials.getHeaderAuth().isEmpty()) { |             if (mCredentials.getHeaderAuth() != null && !mCredentials.getHeaderAuth().isEmpty()) { | ||||||
|                 method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); |                 method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); | ||||||
|  | |||||||
| @ -31,7 +31,6 @@ import android.net.Uri; | |||||||
| 
 | 
 | ||||||
| import com.owncloud.android.lib.common.accounts.AccountUtils; | import com.owncloud.android.lib.common.accounts.AccountUtils; | ||||||
| import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; | import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; | ||||||
| import com.owncloud.android.lib.common.http.HttpClient; |  | ||||||
| import timber.log.Timber; | import timber.log.Timber; | ||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| @ -124,6 +123,24 @@ public class SingleSessionManager { | |||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             Timber.v("reusing client for account %s", accountName); |             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; |             reusingKnown = true; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -112,6 +112,7 @@ public class AccountUtils { | |||||||
|         String username = AccountUtils.getUsernameForAccount(account); |         String username = AccountUtils.getUsernameForAccount(account); | ||||||
| 
 | 
 | ||||||
|         if (isOauth2) { |         if (isOauth2) { | ||||||
|  |             Timber.i("Trying to retrieve credentials for oAuth account" + account.name); | ||||||
|             String accessToken = am.blockingGetAuthToken( |             String accessToken = am.blockingGetAuthToken( | ||||||
|                     account, |                     account, | ||||||
|                     AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), |                     AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), | ||||||
|  | |||||||
| @ -40,6 +40,7 @@ public class HttpConstants { | |||||||
|     public static final String IF_MATCH_HEADER = "If-Match"; |     public static final String IF_MATCH_HEADER = "If-Match"; | ||||||
|     public static final String IF_NONE_MATCH_HEADER = "If-None-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 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 CONTENT_LENGTH_HEADER = "Content-Length"; | ||||||
|     public static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length"; |     public static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length"; | ||||||
|     public static final String OC_X_OC_MTIME_HEADER = "X-OC-Mtime"; |     public static final String OC_X_OC_MTIME_HEADER = "X-OC-Mtime"; | ||||||
|  | |||||||
| @ -51,7 +51,7 @@ class LogInterceptor : Interceptor { | |||||||
| 
 | 
 | ||||||
|         val request = chain.request().also { |         val request = chain.request().also { | ||||||
|             val requestId = it.headers[OC_X_REQUEST_ID] |             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) |             logHeaders(requestId, it.headers, REQUEST) | ||||||
|             logRequestBody(requestId, it.body) |             logRequestBody(requestId, it.body) | ||||||
|         } |         } | ||||||
| @ -64,7 +64,7 @@ class LogInterceptor : Interceptor { | |||||||
|                 RESPONSE, |                 RESPONSE, | ||||||
|                 INFO, |                 INFO, | ||||||
|                 requestId, |                 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) |             logHeaders(requestId, it.headers, RESPONSE) | ||||||
|             logResponseBody(requestId, it.body) |             logResponseBody(requestId, it.body) | ||||||
|  | |||||||
| @ -24,13 +24,36 @@ | |||||||
| package com.owncloud.android.lib.common.http.methods.webdav | package com.owncloud.android.lib.common.http.methods.webdav | ||||||
| 
 | 
 | ||||||
| import at.bitfire.dav4jvm.Property | import at.bitfire.dav4jvm.Property | ||||||
| import at.bitfire.dav4jvm.PropertyUtils.getAllPropSet |  | ||||||
| import at.bitfire.dav4jvm.PropertyUtils.getQuotaPropset | 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 | import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes | ||||||
| 
 | 
 | ||||||
| object DavUtils { | object DavUtils { | ||||||
|     @JvmStatic val allPropset: Array<Property.Name> |     @JvmStatic val allPropSet: Array<Property.Name> | ||||||
|         get() = getAllPropSet().plus(OCShareTypes.NAME) |         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<Property.Name> |     val quotaPropSet: Array<Property.Name> | ||||||
|         get() = getQuotaPropset() |         get() = getQuotaPropset() | ||||||
|  | |||||||
| @ -46,6 +46,7 @@ import java.io.IOException; | |||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
| import java.net.MalformedURLException; | import java.net.MalformedURLException; | ||||||
|  | import java.net.ProtocolException; | ||||||
| import java.net.SocketException; | import java.net.SocketException; | ||||||
| import java.net.SocketTimeoutException; | import java.net.SocketTimeoutException; | ||||||
| import java.net.UnknownHostException; | import java.net.UnknownHostException; | ||||||
| @ -166,7 +167,10 @@ public class RemoteOperationResult<T> | |||||||
|         } else if (e instanceof FileNotFoundException) { |         } else if (e instanceof FileNotFoundException) { | ||||||
|             mCode = ResultCode.LOCAL_FILE_NOT_FOUND; |             mCode = ResultCode.LOCAL_FILE_NOT_FOUND; | ||||||
| 
 | 
 | ||||||
|         } else { |         } else if (e instanceof ProtocolException) { | ||||||
|  |             mCode = ResultCode.NETWORK_ERROR; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|             mCode = ResultCode.UNKNOWN_ERROR; |             mCode = ResultCode.UNKNOWN_ERROR; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -589,5 +593,6 @@ public class RemoteOperationResult<T> | |||||||
|         SPECIFIC_METHOD_NOT_ALLOWED, |         SPECIFIC_METHOD_NOT_ALLOWED, | ||||||
|         SPECIFIC_BAD_REQUEST, |         SPECIFIC_BAD_REQUEST, | ||||||
|         TOO_EARLY, |         TOO_EARLY, | ||||||
|  |         NETWORK_ERROR, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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<String>() { | ||||||
|  | 
 | ||||||
|  |     override fun run(client: OwnCloudClient): RemoteOperationResult<String> { | ||||||
|  |         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<String>(ResultCode.OK).apply { | ||||||
|  |                 val moshi = Moshi.Builder().build() | ||||||
|  |                 val adapter: JsonAdapter<CreateFileWithAppProviderResponse> = moshi.adapter(CreateFileWithAppProviderResponse::class.java) | ||||||
|  | 
 | ||||||
|  |                 data = postMethod.getResponseBodyAsString()?.let { adapter.fromJson(it)!!.fileId } | ||||||
|  |             } | ||||||
|  |             else RemoteOperationResult<String>(postMethod).apply { data = "" } | ||||||
|  | 
 | ||||||
|  |         } catch (e: Exception) { | ||||||
|  |             val result = RemoteOperationResult<String>(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 | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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<AppRegistryResponse>() { | ||||||
|  | 
 | ||||||
|  |     override fun run(client: OwnCloudClient): RemoteOperationResult<AppRegistryResponse> { | ||||||
|  |         var result: RemoteOperationResult<AppRegistryResponse> | ||||||
|  | 
 | ||||||
|  |         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<AppRegistryResponse> = 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" | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,5 +1,5 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /* 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 | *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
| *   of this software and associated documentation files (the "Software"), to deal | *   of this software and associated documentation files (the "Software"), to deal | ||||||
| @ -21,7 +21,8 @@ | |||||||
| *   THE SOFTWARE. | *   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.OwnCloudClient | ||||||
| import com.owncloud.android.lib.common.http.HttpConstants | 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.RemoteOperation | ||||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode | 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.JsonAdapter | ||||||
| import com.squareup.moshi.JsonClass | import com.squareup.moshi.JsonClass | ||||||
| import com.squareup.moshi.Moshi | import com.squareup.moshi.Moshi | ||||||
| @ -41,16 +41,18 @@ import java.net.URL | |||||||
| import java.util.concurrent.TimeUnit | import java.util.concurrent.TimeUnit | ||||||
| 
 | 
 | ||||||
| class GetUrlToOpenInWebRemoteOperation( | class GetUrlToOpenInWebRemoteOperation( | ||||||
|     val openWithWebEndpoint: String, |     private val openWithWebEndpoint: String, | ||||||
|     val fileId: String, |     private val fileId: String, | ||||||
|  |     private val appName: String, | ||||||
| ) : RemoteOperation<String>() { | ) : RemoteOperation<String>() { | ||||||
| 
 | 
 | ||||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<String> { |     override fun run(client: OwnCloudClient): RemoteOperationResult<String> { | ||||||
|         return try { |         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 { |             val postMethod = PostMethod(URL(stringUrl), openInWebRequestBody).apply { | ||||||
|                 setReadTimeout(TIMEOUT, TimeUnit.MILLISECONDS) |                 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 = |         fun toRequestBody(): RequestBody = | ||||||
|             FormBody.Builder().build() |             FormBody.Builder() | ||||||
|  |                 .add(PARAM_FILE_ID, fileId) | ||||||
|  |                 .add(PARAM_APP_NAME, appName) | ||||||
|  |                 .build() | ||||||
| 
 | 
 | ||||||
|         companion object { |         companion object { | ||||||
|             const val PARAM_FILE_ID = "file_id" |             const val PARAM_FILE_ID = "file_id" | ||||||
|  |             const val PARAM_APP_NAME = "app_name" | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -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<AppRegistryMimeTypeResponse> | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | @JsonClass(generateAdapter = true) | ||||||
|  | data class AppRegistryMimeTypeResponse( | ||||||
|  |     @Json(name = "mime_type") val mimeType: String, | ||||||
|  |     val ext: String? = null, | ||||||
|  |     @Json(name = "app_providers") | ||||||
|  |     val appProviders: List<AppRegistryProviderResponse>, | ||||||
|  |     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, | ||||||
|  | ) | ||||||
| @ -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<AppRegistryResponse> | ||||||
|  | 
 | ||||||
|  |     fun getUrlToOpenInWeb( | ||||||
|  |         openWebEndpoint: String, | ||||||
|  |         fileId: String, | ||||||
|  |         appName: String, | ||||||
|  |     ): RemoteOperationResult<String> | ||||||
|  | 
 | ||||||
|  |     fun createFileWithAppProvider( | ||||||
|  |         createFileWithAppProviderEndpoint: String, | ||||||
|  |         parentContainerId: String, | ||||||
|  |         filename: String, | ||||||
|  |     ): RemoteOperationResult<String> | ||||||
|  | } | ||||||
| @ -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<AppRegistryResponse> = | ||||||
|  |         GetRemoteAppRegistryOperation().execute(client) | ||||||
|  | 
 | ||||||
|  |     override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String, appName: String): RemoteOperationResult<String> = | ||||||
|  |         GetUrlToOpenInWebRemoteOperation( | ||||||
|  |             openWithWebEndpoint = openWebEndpoint, | ||||||
|  |             fileId = fileId, | ||||||
|  |             appName = appName | ||||||
|  |         ).execute(client) | ||||||
|  | 
 | ||||||
|  |     override fun createFileWithAppProvider( | ||||||
|  |         createFileWithAppProviderEndpoint: String, | ||||||
|  |         parentContainerId: String, | ||||||
|  |         filename: String | ||||||
|  |     ): RemoteOperationResult<String> = | ||||||
|  |         CreateRemoteFileWithAppProviderOperation( | ||||||
|  |             createFileWithAppProviderEndpoint = createFileWithAppProviderEndpoint, | ||||||
|  |             parentContainerId = parentContainerId, | ||||||
|  |             filename = filename, | ||||||
|  |         ).execute(client) | ||||||
|  | } | ||||||
| @ -1,5 +1,5 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /* 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 | *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
| *   of this software and associated documentation files (the "Software"), to deal | *   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.OwnCloudClient | ||||||
| import com.owncloud.android.lib.common.http.HttpConstants | 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.http.methods.webdav.PropfindMethod | ||||||
| import com.owncloud.android.lib.common.network.WebdavUtils | import com.owncloud.android.lib.common.network.WebdavUtils | ||||||
| import com.owncloud.android.lib.common.operations.RemoteOperation | import com.owncloud.android.lib.common.operations.RemoteOperation | ||||||
| @ -41,6 +41,7 @@ import java.util.concurrent.TimeUnit | |||||||
|  * @author David A. Velasco |  * @author David A. Velasco | ||||||
|  * @author David González Verdugo |  * @author David González Verdugo | ||||||
|  * @author Abel García de Prada |  * @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 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 |  * @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( | class CheckPathExistenceRemoteOperation( | ||||||
|     val remotePath: String? = "", |     val remotePath: String? = "", | ||||||
|     val isUserLoggedIn: Boolean |     val isUserLoggedIn: Boolean, | ||||||
|  |     val spaceWebDavUrl: String? = null, | ||||||
| ) : RemoteOperation<Boolean>() { | ) : RemoteOperation<Boolean>() { | ||||||
| 
 | 
 | ||||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> { |     override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> { | ||||||
|         return try { |         val baseStringUrl = spaceWebDavUrl ?: if (isUserLoggedIn) client.baseFilesWebDavUri.toString() | ||||||
|             val stringUrl = |             else client.userFilesWebDavUri.toString() | ||||||
|                 if (isUserLoggedIn) client.baseFilesWebDavUri.toString() |         val stringUrl = baseStringUrl + WebdavUtils.encodePath(remotePath) | ||||||
|                 else client.userFilesWebDavUri.toString() + 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) |                 setReadTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) | ||||||
|                 setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) |                 setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var status = client.executeHttpMethod(propFindMethod) |             val status = client.executeHttpMethod(propFindMethod) | ||||||
|             /* PROPFIND method |             /* PROPFIND method | ||||||
|              * 404 NOT FOUND: path doesn't exist, |              * 404 NOT FOUND: path doesn't exist, | ||||||
|              * 207 MULTI_STATUS: path exists. |              * 207 MULTI_STATUS: path exists. | ||||||
| @ -77,7 +79,7 @@ class CheckPathExistenceRemoteOperation( | |||||||
|             val result = RemoteOperationResult<Boolean>(e) |             val result = RemoteOperationResult<Boolean>(e) | ||||||
|             Timber.e( |             Timber.e( | ||||||
|                 e, |                 e, | ||||||
|                 "Existence check for ${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)} : ${result.logMessage}" |                 "Existence check for $stringUrl : ${result.logMessage}" | ||||||
|             ) |             ) | ||||||
|             result |             result | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /* 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 |  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  *   of this software and associated documentation files (the "Software"), to deal |  *   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 David A. Velasco | ||||||
|  * @author Christian Schabesberger |  * @author Christian Schabesberger | ||||||
|  * @author David González V. |  * @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. |  * @param targetRemotePath Remote path desired for the file/folder to copy it. | ||||||
|  */ |  */ | ||||||
| class CopyRemoteFileOperation( | class CopyRemoteFileOperation( | ||||||
|     private val srcRemotePath: String, |     private val sourceRemotePath: String, | ||||||
|     private val targetRemotePath: String, |     private val targetRemotePath: String, | ||||||
|  |     private val sourceSpaceWebDavUrl: String? = null, | ||||||
|  |     private val targetSpaceWebDavUrl: String? = null, | ||||||
| ) : RemoteOperation<String>() { | ) : RemoteOperation<String>() { | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Performs the rename operation. |      * Performs the rename operation. | ||||||
|      * |      * | ||||||
|      * @param client Client object to communicate with the remote ownCloud server. |      * @param client Client object to communicate with the remote ownCloud server. | ||||||
|      */ |      */ | ||||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<String> { |     override fun run(client: OwnCloudClient): RemoteOperationResult<String> { | ||||||
|         if (targetRemotePath == srcRemotePath) { |         if (targetRemotePath == sourceRemotePath && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) { | ||||||
|             // nothing to do! |             // nothing to do! | ||||||
|             return RemoteOperationResult(ResultCode.OK) |             return RemoteOperationResult(ResultCode.OK) | ||||||
|         } |         } | ||||||
|         if (targetRemotePath.startsWith(srcRemotePath)) { |         if (targetRemotePath.startsWith(sourceRemotePath) && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) { | ||||||
|             return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT) |             return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -70,8 +74,8 @@ class CopyRemoteFileOperation( | |||||||
|         var result: RemoteOperationResult<String> |         var result: RemoteOperationResult<String> | ||||||
|         try { |         try { | ||||||
|             val copyMethod = CopyMethod( |             val copyMethod = CopyMethod( | ||||||
|                 URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(srcRemotePath)), |                 URL((sourceSpaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(sourceRemotePath)), | ||||||
|                 client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath), |                 (targetSpaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(targetRemotePath), | ||||||
|             ).apply { |             ).apply { | ||||||
|                 setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS) |                 setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS) | ||||||
|                 setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS) |                 setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS) | ||||||
| @ -95,10 +99,10 @@ class CopyRemoteFileOperation( | |||||||
|                     client.exhaustResponse(copyMethod.getResponseBodyAsStream()) |                     client.exhaustResponse(copyMethod.getResponseBodyAsStream()) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             Timber.i("Copy $srcRemotePath to $targetRemotePath: ${result.logMessage}") |             Timber.i("Copy $sourceRemotePath to $targetRemotePath: ${result.logMessage}") | ||||||
|         } catch (e: Exception) { |         } catch (e: Exception) { | ||||||
|             result = RemoteOperationResult(e) |             result = RemoteOperationResult(e) | ||||||
|             Timber.e(e, "Copy $srcRemotePath to $targetRemotePath: ${result.logMessage}") |             Timber.e(e, "Copy $sourceRemotePath to $targetRemotePath: ${result.logMessage}") | ||||||
|         } |         } | ||||||
|         return result |         return result | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -46,7 +46,8 @@ import java.util.concurrent.TimeUnit | |||||||
| class CreateRemoteFolderOperation( | class CreateRemoteFolderOperation( | ||||||
|     val remotePath: String, |     val remotePath: String, | ||||||
|     private val createFullPath: Boolean, |     private val createFullPath: Boolean, | ||||||
|     private val isChunksFolder: Boolean = false |     private val isChunksFolder: Boolean = false, | ||||||
|  |     val spaceWebDavUrl: String? = null, | ||||||
| ) : RemoteOperation<Unit>() { | ) : RemoteOperation<Unit>() { | ||||||
| 
 | 
 | ||||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> { |     override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> { | ||||||
| @ -67,13 +68,13 @@ class CreateRemoteFolderOperation( | |||||||
|         var result: RemoteOperationResult<Unit> |         var result: RemoteOperationResult<Unit> | ||||||
|         try { |         try { | ||||||
|             val webDavUri = if (isChunksFolder) { |             val webDavUri = if (isChunksFolder) { | ||||||
|                 client.uploadsWebDavUri |                 client.uploadsWebDavUri.toString() | ||||||
|             } else { |             } else { | ||||||
|                 client.userFilesWebDavUri |                 spaceWebDavUrl ?: client.userFilesWebDavUri.toString() | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             val mkCol = MkColMethod( |             val mkCol = MkColMethod( | ||||||
|                 URL(webDavUri.toString() + WebdavUtils.encodePath(remotePath)) |                 URL(webDavUri + WebdavUtils.encodePath(remotePath)) | ||||||
|             ).apply { |             ).apply { | ||||||
|                 setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS) |                 setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS) | ||||||
|                 setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS) |                 setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS) | ||||||
|  | |||||||
| @ -36,7 +36,6 @@ import java.io.BufferedInputStream | |||||||
| import java.io.File | import java.io.File | ||||||
| import java.io.FileOutputStream | import java.io.FileOutputStream | ||||||
| import java.net.URL | import java.net.URL | ||||||
| import java.util.HashSet |  | ||||||
| import java.util.concurrent.atomic.AtomicBoolean | import java.util.concurrent.atomic.AtomicBoolean | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -47,7 +46,8 @@ import java.util.concurrent.atomic.AtomicBoolean | |||||||
|  */ |  */ | ||||||
| class DownloadRemoteFileOperation( | class DownloadRemoteFileOperation( | ||||||
|     private val remotePath: String, |     private val remotePath: String, | ||||||
|     localFolderPath: String |     localFolderPath: String, | ||||||
|  |     private val spaceWebDavUrl: String? = null, | ||||||
| ) : RemoteOperation<Unit>() { | ) : RemoteOperation<Unit>() { | ||||||
| 
 | 
 | ||||||
|     private val cancellationRequested = AtomicBoolean(false) |     private val cancellationRequested = AtomicBoolean(false) | ||||||
| @ -84,7 +84,8 @@ class DownloadRemoteFileOperation( | |||||||
|         var bis: BufferedInputStream? = null |         var bis: BufferedInputStream? = null | ||||||
|         var savedFile = false |         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 { |         try { | ||||||
|             val status = client.executeHttpMethod(getMethod) |             val status = client.executeHttpMethod(getMethod) | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ class GetBaseUrlRemoteOperation : RemoteOperation<String?>() { | |||||||
|         return try { |         return try { | ||||||
|             val stringUrl = client.baseFilesWebDavUri.toString() |             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) |                 setReadTimeout(TIMEOUT, TimeUnit.SECONDS) | ||||||
|                 setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS) |                 setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS) | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /* 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 |  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  *   of this software and associated documentation files (the "Software"), to deal |  *   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 |  * 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. |  * Allows renaming the moving file/folder at the same time. | ||||||
|  * |  * | ||||||
|  * @author David A. Velasco |  * @author David A. Velasco | ||||||
|  * @author David González Verdugo |  * @author David González Verdugo | ||||||
|  * @author Abel García de Prada |  * @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( | open class MoveRemoteFileOperation( | ||||||
|     private val sourceRemotePath: String, |     private val sourceRemotePath: String, | ||||||
|     private val targetRemotePath: String, |     private val targetRemotePath: String, | ||||||
|  |     private val spaceWebDavUrl: String? = null, | ||||||
| ) : RemoteOperation<Unit>() { | ) : RemoteOperation<Unit>() { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -73,8 +78,8 @@ open class MoveRemoteFileOperation( | |||||||
|             // so this uri has to be customizable |             // so this uri has to be customizable | ||||||
|             val srcWebDavUri = getSrcWebDavUriForClient(client) |             val srcWebDavUri = getSrcWebDavUriForClient(client) | ||||||
|             val moveMethod = MoveMethod( |             val moveMethod = MoveMethod( | ||||||
|                 url = URL(srcWebDavUri.toString() + WebdavUtils.encodePath(sourceRemotePath)), |                 url = URL((spaceWebDavUrl ?: srcWebDavUri.toString()) + WebdavUtils.encodePath(sourceRemotePath)), | ||||||
|                 destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath), |                 destinationUrl = (spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(targetRemotePath), | ||||||
|             ).apply { |             ).apply { | ||||||
|                 addRequestHeaders(this) |                 addRequestHeaders(this) | ||||||
|                 setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS) |                 setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS) | ||||||
|  | |||||||
| @ -46,7 +46,10 @@ import java.util.concurrent.TimeUnit | |||||||
|  * @author David González Verdugo |  * @author David González Verdugo | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation<RemoteFile>() { | class ReadRemoteFileOperation( | ||||||
|  |     val remotePath: String, | ||||||
|  |     val spaceWebDavUrl: String? = null, | ||||||
|  | ) : RemoteOperation<RemoteFile>() { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Performs the read operation. |      * Performs the read operation. | ||||||
| @ -57,9 +60,9 @@ class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation<RemoteFi | |||||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteFile> { |     override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteFile> { | ||||||
|         try { |         try { | ||||||
|             val propFind = PropfindMethod( |             val propFind = PropfindMethod( | ||||||
|                 url = URL("${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)}"), |                 url = getFinalWebDavUrl(), | ||||||
|                 depth = DEPTH_0, |                 depth = DEPTH_0, | ||||||
|                 propertiesToRequest = DavUtils.allPropset |                 propertiesToRequest = DavUtils.allPropSet | ||||||
|             ).apply { |             ).apply { | ||||||
|                 setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS) |                 setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS) | ||||||
|                 setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS) |                 setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS) | ||||||
| @ -69,10 +72,11 @@ class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation<RemoteFi | |||||||
|             Timber.i("Read remote file $remotePath with status ${propFind.statusCode}") |             Timber.i("Read remote file $remotePath with status ${propFind.statusCode}") | ||||||
| 
 | 
 | ||||||
|             return if (isSuccess(status)) { |             return if (isSuccess(status)) { | ||||||
|                 // TODO: Remove that !! |  | ||||||
|                 val remoteFile = RemoteFile.getRemoteFileFromDav( |                 val remoteFile = RemoteFile.getRemoteFileFromDav( | ||||||
|                     propFind.root!!, |                     davResource = propFind.root!!, | ||||||
|                     AccountUtils.getUserId(mAccount, mContext), mAccount.name |                     userId = AccountUtils.getUserId(mAccount, mContext), | ||||||
|  |                     userName = mAccount.name, | ||||||
|  |                     spaceWebDavUrl = spaceWebDavUrl, | ||||||
|                 ) |                 ) | ||||||
| 
 | 
 | ||||||
|                 RemoteOperationResult<RemoteFile>(RemoteOperationResult.ResultCode.OK).apply { |                 RemoteOperationResult<RemoteFile>(RemoteOperationResult.ResultCode.OK).apply { | ||||||
| @ -88,6 +92,12 @@ class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation<RemoteFi | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private fun getFinalWebDavUrl(): URL { | ||||||
|  |         val baseWebDavUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString() | ||||||
|  | 
 | ||||||
|  |         return URL(baseWebDavUrl + WebdavUtils.encodePath(remotePath)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private fun isSuccess(status: Int) = status.isOneOf(HTTP_MULTI_STATUS, HTTP_OK) |     private fun isSuccess(status: Int) = status.isOneOf(HTTP_MULTI_STATUS, HTTP_OK) | ||||||
| 
 | 
 | ||||||
|     companion object { |     companion object { | ||||||
|  | |||||||
| @ -48,7 +48,8 @@ import java.net.URL | |||||||
|  * @author David González Verdugo |  * @author David González Verdugo | ||||||
|  */ |  */ | ||||||
| class ReadRemoteFolderOperation( | class ReadRemoteFolderOperation( | ||||||
|     val remotePath: String |     val remotePath: String, | ||||||
|  |     val spaceWebDavUrl: String? = null, | ||||||
| ) : RemoteOperation<ArrayList<RemoteFile>>() { | ) : RemoteOperation<ArrayList<RemoteFile>>() { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -61,9 +62,9 @@ class ReadRemoteFolderOperation( | |||||||
|             PropertyRegistry.register(OCShareTypes.Factory()) |             PropertyRegistry.register(OCShareTypes.Factory()) | ||||||
| 
 | 
 | ||||||
|             val propfindMethod = PropfindMethod( |             val propfindMethod = PropfindMethod( | ||||||
|                 URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)), |                 getFinalWebDavUrl(), | ||||||
|                 DavConstants.DEPTH_1, |                 DavConstants.DEPTH_1, | ||||||
|                 DavUtils.allPropset |                 DavUtils.allPropSet | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|             val status = client.executeHttpMethod(propfindMethod) |             val status = client.executeHttpMethod(propfindMethod) | ||||||
| @ -71,12 +72,11 @@ class ReadRemoteFolderOperation( | |||||||
|             if (isSuccess(status)) { |             if (isSuccess(status)) { | ||||||
|                 val mFolderAndFiles = ArrayList<RemoteFile>() |                 val mFolderAndFiles = ArrayList<RemoteFile>() | ||||||
| 
 | 
 | ||||||
|                 // parse data from remote folder |  | ||||||
|                 // TODO: Remove that !! |  | ||||||
|                 val remoteFolder = RemoteFile.getRemoteFileFromDav( |                 val remoteFolder = RemoteFile.getRemoteFileFromDav( | ||||||
|                     davResource = propfindMethod.root!!, |                     davResource = propfindMethod.root!!, | ||||||
|                     userId = AccountUtils.getUserId(mAccount, mContext), |                     userId = AccountUtils.getUserId(mAccount, mContext), | ||||||
|                     userName = mAccount.name |                     userName = mAccount.name, | ||||||
|  |                     spaceWebDavUrl = spaceWebDavUrl, | ||||||
|                 ) |                 ) | ||||||
|                 mFolderAndFiles.add(remoteFolder) |                 mFolderAndFiles.add(remoteFolder) | ||||||
| 
 | 
 | ||||||
| @ -85,7 +85,8 @@ class ReadRemoteFolderOperation( | |||||||
|                     val remoteFile = RemoteFile.getRemoteFileFromDav( |                     val remoteFile = RemoteFile.getRemoteFileFromDav( | ||||||
|                         davResource = resource, |                         davResource = resource, | ||||||
|                         userId = AccountUtils.getUserId(mAccount, mContext), |                         userId = AccountUtils.getUserId(mAccount, mContext), | ||||||
|                         userName = mAccount.name |                         userName = mAccount.name, | ||||||
|  |                         spaceWebDavUrl = spaceWebDavUrl, | ||||||
|                     ) |                     ) | ||||||
|                     mFolderAndFiles.add(remoteFile) |                     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) |     private fun isSuccess(status: Int): Boolean = status.isOneOf(HTTP_OK, HTTP_MULTI_STATUS) | ||||||
| } | } | ||||||
|  | |||||||
| @ -26,6 +26,7 @@ package com.owncloud.android.lib.resources.files | |||||||
| 
 | 
 | ||||||
| import android.net.Uri | import android.net.Uri | ||||||
| import android.os.Parcelable | import android.os.Parcelable | ||||||
|  | import androidx.annotation.VisibleForTesting | ||||||
| import at.bitfire.dav4jvm.PropStat | import at.bitfire.dav4jvm.PropStat | ||||||
| import at.bitfire.dav4jvm.Property | import at.bitfire.dav4jvm.Property | ||||||
| import at.bitfire.dav4jvm.Response | 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.OCPermissions | ||||||
| import at.bitfire.dav4jvm.property.OCPrivatelink | import at.bitfire.dav4jvm.property.OCPrivatelink | ||||||
| import at.bitfire.dav4jvm.property.OCSize | 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.OwnCloudClient | ||||||
| import com.owncloud.android.lib.common.http.HttpConstants | import com.owncloud.android.lib.common.http.HttpConstants | ||||||
| import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes | import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes | ||||||
| @ -50,7 +49,6 @@ import kotlinx.parcelize.Parcelize | |||||||
| import okhttp3.HttpUrl | import okhttp3.HttpUrl | ||||||
| import timber.log.Timber | import timber.log.Timber | ||||||
| import java.io.File | import java.io.File | ||||||
| import java.math.BigDecimal |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Contains the data of a Remote File from a WebDavEntry |  * Contains the data of a Remote File from a WebDavEntry | ||||||
| @ -72,8 +70,6 @@ data class RemoteFile( | |||||||
|     var permissions: String? = null, |     var permissions: String? = null, | ||||||
|     var remoteId: String? = null, |     var remoteId: String? = null, | ||||||
|     var size: Long = 0, |     var size: Long = 0, | ||||||
|     var quotaUsedBytes: BigDecimal? = null, |  | ||||||
|     var quotaAvailableBytes: BigDecimal? = null, |  | ||||||
|     var privateLink: String? = null, |     var privateLink: String? = null, | ||||||
|     var owner: String, |     var owner: String, | ||||||
|     var sharedByLink: Boolean = false, |     var sharedByLink: Boolean = false, | ||||||
| @ -100,8 +96,13 @@ data class RemoteFile( | |||||||
|         const val MIME_DIR = "DIR" |         const val MIME_DIR = "DIR" | ||||||
|         const val MIME_DIR_UNIX = "httpd/unix-directory" |         const val MIME_DIR_UNIX = "httpd/unix-directory" | ||||||
| 
 | 
 | ||||||
|         fun getRemoteFileFromDav(davResource: Response, userId: String, userName: String): RemoteFile { |         fun getRemoteFileFromDav( | ||||||
|             val remotePath = getRemotePathFromUrl(davResource.href, userId) |             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 remoteFile = RemoteFile(remotePath = remotePath, owner = userName) | ||||||
|             val properties = getPropertiesEvenIfPostProcessing(davResource) |             val properties = getPropertiesEvenIfPostProcessing(davResource) | ||||||
| 
 | 
 | ||||||
| @ -131,12 +132,6 @@ data class RemoteFile( | |||||||
|                     is OCSize -> { |                     is OCSize -> { | ||||||
|                         remoteFile.size = property.size |                         remoteFile.size = property.size | ||||||
|                     } |                     } | ||||||
|                     is QuotaUsedBytes -> { |  | ||||||
|                         remoteFile.quotaUsedBytes = BigDecimal.valueOf(property.quotaUsedBytes) |  | ||||||
|                     } |  | ||||||
|                     is QuotaAvailableBytes -> { |  | ||||||
|                         remoteFile.quotaAvailableBytes = BigDecimal.valueOf(property.quotaAvailableBytes) |  | ||||||
|                     } |  | ||||||
|                     is OCPrivatelink -> { |                     is OCPrivatelink -> { | ||||||
|                         remoteFile.privateLink = property.link |                         remoteFile.privateLink = property.link | ||||||
|                     } |                     } | ||||||
| @ -164,15 +159,25 @@ data class RemoteFile( | |||||||
|          * Retrieves a relative path from a remote file url |          * 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 url    remote file url | ||||||
|          * @param userId file owner |          * @param userId file owner | ||||||
|  |          * @param spaceWebDavUrl custom web dav url for space | ||||||
|          * @return remote relative path of the file |          * @return remote relative path of the file | ||||||
|          */ |          */ | ||||||
|         private fun getRemotePathFromUrl(url: HttpUrl, userId: String): String { |         @VisibleForTesting | ||||||
|             val davFilesPath = OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId |         fun getRemotePathFromUrl( | ||||||
|             val absoluteDavPath = Uri.decode(url.encodedPath) |             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() |             val pathToOc = absoluteDavPath.split(davFilesPath).first() | ||||||
|             return absoluteDavPath.replace(pathToOc + davFilesPath, "") |             return absoluteDavPath.replace(pathToOc + davFilesPath, "") | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -24,7 +24,6 @@ | |||||||
| 
 | 
 | ||||||
| package com.owncloud.android.lib.resources.files | package com.owncloud.android.lib.resources.files | ||||||
| 
 | 
 | ||||||
| import android.net.Uri |  | ||||||
| import com.owncloud.android.lib.common.OwnCloudClient | 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_NO_CONTENT | ||||||
| import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK | import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK | ||||||
| @ -46,7 +45,8 @@ import java.net.URL | |||||||
|  * @author Abel García de Prada |  * @author Abel García de Prada | ||||||
|  */ |  */ | ||||||
| open class RemoveRemoteFileOperation( | open class RemoveRemoteFileOperation( | ||||||
|     private val remotePath: String |     private val remotePath: String, | ||||||
|  |     val spaceWebDavUrl: String? = null, | ||||||
| ) : RemoteOperation<Unit>() { | ) : RemoteOperation<Unit>() { | ||||||
| 
 | 
 | ||||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> { |     override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> { | ||||||
| @ -54,7 +54,7 @@ open class RemoveRemoteFileOperation( | |||||||
|         try { |         try { | ||||||
|             val srcWebDavUri = getSrcWebDavUriForClient(client) |             val srcWebDavUri = getSrcWebDavUriForClient(client) | ||||||
|             val deleteMethod = DeleteMethod( |             val deleteMethod = DeleteMethod( | ||||||
|                 URL(srcWebDavUri.toString() + WebdavUtils.encodePath(remotePath)) |                 URL(srcWebDavUri + WebdavUtils.encodePath(remotePath)) | ||||||
|             ) |             ) | ||||||
|             val status = client.executeHttpMethod(deleteMethod) |             val status = client.executeHttpMethod(deleteMethod) | ||||||
| 
 | 
 | ||||||
| @ -75,7 +75,7 @@ open class RemoveRemoteFileOperation( | |||||||
|      * For standard removals, we will use [OwnCloudClient.getUserFilesWebDavUri]. |      * For standard removals, we will use [OwnCloudClient.getUserFilesWebDavUri]. | ||||||
|      * In case we need a different source Uri, override this method. |      * 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) |     private fun isSuccess(status: Int) = status.isOneOf(HTTP_OK, HTTP_NO_CONTENT) | ||||||
| } | } | ||||||
|  | |||||||
| @ -48,6 +48,7 @@ class RenameRemoteFileOperation( | |||||||
|     private val oldRemotePath: String, |     private val oldRemotePath: String, | ||||||
|     private val newName: String, |     private val newName: String, | ||||||
|     isFolder: Boolean, |     isFolder: Boolean, | ||||||
|  |     val spaceWebDavUrl: String? = null, | ||||||
| ) : RemoteOperation<Unit>() { | ) : RemoteOperation<Unit>() { | ||||||
| 
 | 
 | ||||||
|     private var newRemotePath: String |     private var newRemotePath: String | ||||||
| @ -75,8 +76,8 @@ class RenameRemoteFileOperation( | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             val moveMethod: MoveMethod = MoveMethod( |             val moveMethod: MoveMethod = MoveMethod( | ||||||
|                 url = URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(oldRemotePath)), |                 url = URL((spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(oldRemotePath)), | ||||||
|                 destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(newRemotePath), |                 destinationUrl = (spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(newRemotePath), | ||||||
|             ).apply { |             ).apply { | ||||||
|                 setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.MILLISECONDS) |                 setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.MILLISECONDS) | ||||||
|                 setConnectionTimeout(RENAME_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS) |                 setConnectionTimeout(RENAME_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS) | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /* 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 |  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  *   of this software and associated documentation files (the "Software"), to deal |  *   of this software and associated documentation files (the "Software"), to deal | ||||||
| @ -48,6 +48,7 @@ import java.util.concurrent.atomic.AtomicBoolean | |||||||
|  * @author masensio |  * @author masensio | ||||||
|  * @author David González Verdugo |  * @author David González Verdugo | ||||||
|  * @author Abel García de Prada |  * @author Abel García de Prada | ||||||
|  |  * @author Juan Carlos Garrote Gascón | ||||||
|  */ |  */ | ||||||
| open class UploadFileFromFileSystemOperation( | open class UploadFileFromFileSystemOperation( | ||||||
|     val localPath: String, |     val localPath: String, | ||||||
| @ -55,6 +56,7 @@ open class UploadFileFromFileSystemOperation( | |||||||
|     val mimeType: String, |     val mimeType: String, | ||||||
|     val lastModifiedTimestamp: String, |     val lastModifiedTimestamp: String, | ||||||
|     val requiredEtag: String?, |     val requiredEtag: String?, | ||||||
|  |     val spaceWebDavUrl: String? = null, | ||||||
| ) : RemoteOperation<Unit>() { | ) : RemoteOperation<Unit>() { | ||||||
| 
 | 
 | ||||||
|     protected val cancellationRequested = AtomicBoolean(false) |     protected val cancellationRequested = AtomicBoolean(false) | ||||||
| @ -97,7 +99,8 @@ open class UploadFileFromFileSystemOperation( | |||||||
|             synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) } |             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 |             retryOnConnectionFailure = false | ||||||
|             if (!requiredEtag.isNullOrBlank()) { |             if (!requiredEtag.isNullOrBlank()) { | ||||||
|                 addRequestHeader(HttpConstants.IF_MATCH_HEADER, requiredEtag) |                 addRequestHeader(HttpConstants.IF_MATCH_HEADER, requiredEtag) | ||||||
|  | |||||||
| @ -24,10 +24,9 @@ | |||||||
|  */ |  */ | ||||||
| package com.owncloud.android.lib.resources.files.chunks | package com.owncloud.android.lib.resources.files.chunks | ||||||
| 
 | 
 | ||||||
| import android.net.Uri |  | ||||||
| import com.owncloud.android.lib.common.OwnCloudClient | import com.owncloud.android.lib.common.OwnCloudClient | ||||||
| import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation | import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation | ||||||
| 
 | 
 | ||||||
| class RemoveRemoteChunksFolderOperation(remotePath: String) : RemoveRemoteFileOperation(remotePath) { | class RemoveRemoteChunksFolderOperation(remotePath: String) : RemoveRemoteFileOperation(remotePath) { | ||||||
|     override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri |     override fun getSrcWebDavUriForClient(client: OwnCloudClient): String = client.uploadsWebDavUri.toString() | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /* 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 |  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  *   of this software and associated documentation files (the "Software"), to deal |  *   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 | import com.owncloud.android.lib.resources.files.RemoteFile | ||||||
| 
 | 
 | ||||||
| interface FileService : Service { | interface FileService : Service { | ||||||
|     fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult<String> |  | ||||||
| 
 |  | ||||||
|     fun checkPathExistence( |     fun checkPathExistence( | ||||||
|         path: String, |         path: String, | ||||||
|         isUserLogged: Boolean |         isUserLogged: Boolean, | ||||||
|  |         spaceWebDavUrl: String? = null, | ||||||
|     ): RemoteOperationResult<Boolean> |     ): RemoteOperationResult<Boolean> | ||||||
| 
 | 
 | ||||||
|     fun copyFile( |     fun copyFile( | ||||||
|         sourceRemotePath: String, |         sourceRemotePath: String, | ||||||
|         targetRemotePath: String, |         targetRemotePath: String, | ||||||
|  |         sourceSpaceWebDavUrl: String?, | ||||||
|  |         targetSpaceWebDavUrl: String?, | ||||||
|     ): RemoteOperationResult<String> |     ): RemoteOperationResult<String> | ||||||
| 
 | 
 | ||||||
|     fun createFolder( |     fun createFolder( | ||||||
|         remotePath: String, |         remotePath: String, | ||||||
|         createFullPath: Boolean, |         createFullPath: Boolean, | ||||||
|         isChunkFolder: Boolean = false |         isChunkFolder: Boolean = false, | ||||||
|  |         spaceWebDavUrl: String? = null, | ||||||
|     ): RemoteOperationResult<Unit> |     ): RemoteOperationResult<Unit> | ||||||
| 
 | 
 | ||||||
|     fun downloadFile( |     fun downloadFile( | ||||||
| @ -54,18 +56,22 @@ interface FileService : Service { | |||||||
|     fun moveFile( |     fun moveFile( | ||||||
|         sourceRemotePath: String, |         sourceRemotePath: String, | ||||||
|         targetRemotePath: String, |         targetRemotePath: String, | ||||||
|  |         spaceWebDavUrl: String?, | ||||||
|     ): RemoteOperationResult<Unit> |     ): RemoteOperationResult<Unit> | ||||||
| 
 | 
 | ||||||
|     fun readFile( |     fun readFile( | ||||||
|         remotePath: String |         remotePath: String, | ||||||
|  |         spaceWebDavUrl: String? = null, | ||||||
|     ): RemoteOperationResult<RemoteFile> |     ): RemoteOperationResult<RemoteFile> | ||||||
| 
 | 
 | ||||||
|     fun refreshFolder( |     fun refreshFolder( | ||||||
|         remotePath: String |         remotePath: String, | ||||||
|  |         spaceWebDavUrl: String? = null, | ||||||
|     ): RemoteOperationResult<ArrayList<RemoteFile>> |     ): RemoteOperationResult<ArrayList<RemoteFile>> | ||||||
| 
 | 
 | ||||||
|     fun removeFile( |     fun removeFile( | ||||||
|         remotePath: String |         remotePath: String, | ||||||
|  |         spaceWebDavUrl: String? = null, | ||||||
|     ): RemoteOperationResult<Unit> |     ): RemoteOperationResult<Unit> | ||||||
| 
 | 
 | ||||||
|     fun renameFile( |     fun renameFile( | ||||||
| @ -73,5 +79,6 @@ interface FileService : Service { | |||||||
|         oldRemotePath: String, |         oldRemotePath: String, | ||||||
|         newName: String, |         newName: String, | ||||||
|         isFolder: Boolean, |         isFolder: Boolean, | ||||||
|  |         spaceWebDavUrl: String? = null, | ||||||
|     ): RemoteOperationResult<Unit> |     ): RemoteOperationResult<Unit> | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /* 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 |  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  *   of this software and associated documentation files (the "Software"), to deal |  *   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.CopyRemoteFileOperation | ||||||
| import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation | import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation | ||||||
| import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation | 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.MoveRemoteFileOperation | ||||||
| import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation | import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation | ||||||
| import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation | 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 | import com.owncloud.android.lib.resources.files.services.FileService | ||||||
| 
 | 
 | ||||||
| class OCFileService(override val client: OwnCloudClient) : FileService { | class OCFileService(override val client: OwnCloudClient) : FileService { | ||||||
| 
 |  | ||||||
|     override fun checkPathExistence( |     override fun checkPathExistence( | ||||||
|         path: String, |         path: String, | ||||||
|         isUserLogged: Boolean |         isUserLogged: Boolean, | ||||||
|  |         spaceWebDavUrl: String?, | ||||||
|     ): RemoteOperationResult<Boolean> = |     ): RemoteOperationResult<Boolean> = | ||||||
|         CheckPathExistenceRemoteOperation( |         CheckPathExistenceRemoteOperation( | ||||||
|             remotePath = path, |             remotePath = path, | ||||||
|             isUserLoggedIn = isUserLogged |             isUserLoggedIn = isUserLogged, | ||||||
|  |             spaceWebDavUrl = spaceWebDavUrl, | ||||||
|         ).execute(client) |         ).execute(client) | ||||||
| 
 | 
 | ||||||
|     override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult<String> = |  | ||||||
|         GetUrlToOpenInWebRemoteOperation(openWithWebEndpoint = openWebEndpoint, fileId = fileId).execute(client) |  | ||||||
| 
 |  | ||||||
|     override fun copyFile( |     override fun copyFile( | ||||||
|         sourceRemotePath: String, |         sourceRemotePath: String, | ||||||
|         targetRemotePath: String |         targetRemotePath: String, | ||||||
|  |         sourceSpaceWebDavUrl: String?, | ||||||
|  |         targetSpaceWebDavUrl: String?, | ||||||
|     ): RemoteOperationResult<String> = |     ): RemoteOperationResult<String> = | ||||||
|         CopyRemoteFileOperation( |         CopyRemoteFileOperation( | ||||||
|             srcRemotePath = sourceRemotePath, |             sourceRemotePath = sourceRemotePath, | ||||||
|             targetRemotePath = targetRemotePath |             targetRemotePath = targetRemotePath, | ||||||
|  |             sourceSpaceWebDavUrl = sourceSpaceWebDavUrl, | ||||||
|  |             targetSpaceWebDavUrl = targetSpaceWebDavUrl, | ||||||
|         ).execute(client) |         ).execute(client) | ||||||
| 
 | 
 | ||||||
|     override fun createFolder( |     override fun createFolder( | ||||||
|         remotePath: String, |         remotePath: String, | ||||||
|         createFullPath: Boolean, |         createFullPath: Boolean, | ||||||
|         isChunkFolder: Boolean |         isChunkFolder: Boolean, | ||||||
|  |         spaceWebDavUrl: String?, | ||||||
|     ): RemoteOperationResult<Unit> = |     ): RemoteOperationResult<Unit> = | ||||||
|         CreateRemoteFolderOperation( |         CreateRemoteFolderOperation( | ||||||
|             remotePath = remotePath, |             remotePath = remotePath, | ||||||
|             createFullPath = createFullPath, |             createFullPath = createFullPath, | ||||||
|             isChunksFolder = isChunkFolder |             isChunksFolder = isChunkFolder, | ||||||
|  |             spaceWebDavUrl = spaceWebDavUrl, | ||||||
|         ).execute(client) |         ).execute(client) | ||||||
| 
 | 
 | ||||||
|     override fun downloadFile( |     override fun downloadFile( | ||||||
| @ -84,43 +87,53 @@ class OCFileService(override val client: OwnCloudClient) : FileService { | |||||||
|     override fun moveFile( |     override fun moveFile( | ||||||
|         sourceRemotePath: String, |         sourceRemotePath: String, | ||||||
|         targetRemotePath: String, |         targetRemotePath: String, | ||||||
|  |         spaceWebDavUrl: String?, | ||||||
|     ): RemoteOperationResult<Unit> = |     ): RemoteOperationResult<Unit> = | ||||||
|         MoveRemoteFileOperation( |         MoveRemoteFileOperation( | ||||||
|             sourceRemotePath = sourceRemotePath, |             sourceRemotePath = sourceRemotePath, | ||||||
|             targetRemotePath = targetRemotePath, |             targetRemotePath = targetRemotePath, | ||||||
|  |             spaceWebDavUrl = spaceWebDavUrl, | ||||||
|         ).execute(client) |         ).execute(client) | ||||||
| 
 | 
 | ||||||
|     override fun readFile( |     override fun readFile( | ||||||
|         remotePath: String |         remotePath: String, | ||||||
|  |         spaceWebDavUrl: String?, | ||||||
|     ): RemoteOperationResult<RemoteFile> = |     ): RemoteOperationResult<RemoteFile> = | ||||||
|         ReadRemoteFileOperation( |         ReadRemoteFileOperation( | ||||||
|             remotePath = remotePath |             remotePath = remotePath, | ||||||
|  |             spaceWebDavUrl = spaceWebDavUrl, | ||||||
|         ).execute(client) |         ).execute(client) | ||||||
| 
 | 
 | ||||||
|     override fun refreshFolder( |     override fun refreshFolder( | ||||||
|         remotePath: String |         remotePath: String, | ||||||
|  |         spaceWebDavUrl: String?, | ||||||
|     ): RemoteOperationResult<ArrayList<RemoteFile>> = |     ): RemoteOperationResult<ArrayList<RemoteFile>> = | ||||||
|         ReadRemoteFolderOperation( |         ReadRemoteFolderOperation( | ||||||
|             remotePath = remotePath |             remotePath = remotePath, | ||||||
|  |             spaceWebDavUrl = spaceWebDavUrl, | ||||||
|         ).execute(client) |         ).execute(client) | ||||||
| 
 | 
 | ||||||
|     override fun removeFile( |     override fun removeFile( | ||||||
|         remotePath: String |         remotePath: String, | ||||||
|  |         spaceWebDavUrl: String?, | ||||||
|     ): RemoteOperationResult<Unit> = |     ): RemoteOperationResult<Unit> = | ||||||
|         RemoveRemoteFileOperation( |         RemoveRemoteFileOperation( | ||||||
|             remotePath = remotePath |             remotePath = remotePath, | ||||||
|  |             spaceWebDavUrl = spaceWebDavUrl, | ||||||
|         ).execute(client) |         ).execute(client) | ||||||
| 
 | 
 | ||||||
|     override fun renameFile( |     override fun renameFile( | ||||||
|         oldName: String, |         oldName: String, | ||||||
|         oldRemotePath: String, |         oldRemotePath: String, | ||||||
|         newName: String, |         newName: String, | ||||||
|         isFolder: Boolean |         isFolder: Boolean, | ||||||
|  |         spaceWebDavUrl: String?, | ||||||
|     ): RemoteOperationResult<Unit> = |     ): RemoteOperationResult<Unit> = | ||||||
|         RenameRemoteFileOperation( |         RenameRemoteFileOperation( | ||||||
|             oldName = oldName, |             oldName = oldName, | ||||||
|             oldRemotePath = oldRemotePath, |             oldRemotePath = oldRemotePath, | ||||||
|             newName = newName, |             newName = newName, | ||||||
|             isFolder = isFolder |             isFolder = isFolder, | ||||||
|  |             spaceWebDavUrl = spaceWebDavUrl, | ||||||
|         ).execute(client) |         ).execute(client) | ||||||
| } | } | ||||||
|  | |||||||
| @ -89,8 +89,6 @@ class CreateRemoteShareOperation( | |||||||
| 
 | 
 | ||||||
|     var expirationDateInMillis: Long = INIT_EXPIRATION_DATE_IN_MILLIS // Expiration date to set for the public link |     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 |     var retrieveShareDetails = false // To retrieve more info about the just created share | ||||||
| 
 | 
 | ||||||
|     private fun buildRequestUri(baseUri: Uri) = |     private fun buildRequestUri(baseUri: Uri) = | ||||||
| @ -99,7 +97,7 @@ class CreateRemoteShareOperation( | |||||||
|             .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) |             .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) | ||||||
|             .build() |             .build() | ||||||
| 
 | 
 | ||||||
|     private fun parseResponse(response: String): ShareResponse? { |     private fun parseResponse(response: String): ShareResponse { | ||||||
|         val moshi = Moshi.Builder().build() |         val moshi = Moshi.Builder().build() | ||||||
|         val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareItem::class.java) |         val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareItem::class.java) | ||||||
|         val adapter: JsonAdapter<CommonOcsResponse<ShareItem>> = moshi.adapter(commonOcsType) |         val adapter: JsonAdapter<CommonOcsResponse<ShareItem>> = moshi.adapter(commonOcsType) | ||||||
| @ -155,12 +153,10 @@ class CreateRemoteShareOperation( | |||||||
|             formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate) |             formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (publicUpload) { |  | ||||||
|             formBodyBuilder.add(PARAM_PUBLIC_UPLOAD, publicUpload.toString()) |  | ||||||
|         } |  | ||||||
|         if (password.isNotEmpty()) { |         if (password.isNotEmpty()) { | ||||||
|             formBodyBuilder.add(PARAM_PASSWORD, password) |             formBodyBuilder.add(PARAM_PASSWORD, password) | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|         if (RemoteShare.DEFAULT_PERMISSION != permissions) { |         if (RemoteShare.DEFAULT_PERMISSION != permissions) { | ||||||
|             formBodyBuilder.add(PARAM_PERMISSIONS, permissions.toString()) |             formBodyBuilder.add(PARAM_PERMISSIONS, permissions.toString()) | ||||||
|         } |         } | ||||||
| @ -207,7 +203,6 @@ class CreateRemoteShareOperation( | |||||||
|         private const val PARAM_SHARE_TYPE = "shareType" |         private const val PARAM_SHARE_TYPE = "shareType" | ||||||
|         private const val PARAM_SHARE_WITH = "shareWith" |         private const val PARAM_SHARE_WITH = "shareWith" | ||||||
|         private const val PARAM_PASSWORD = "password" |         private const val PARAM_PASSWORD = "password" | ||||||
|         private const val PARAM_PUBLIC_UPLOAD = "publicUpload" |  | ||||||
|         private const val PARAM_PERMISSIONS = "permissions" |         private const val PARAM_PERMISSIONS = "permissions" | ||||||
| 
 | 
 | ||||||
|         //Arguments - constant values |         //Arguments - constant values | ||||||
|  | |||||||
| @ -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.http.methods.nonwebdav.DeleteMethod | ||||||
| import com.owncloud.android.lib.common.operations.RemoteOperation | import com.owncloud.android.lib.common.operations.RemoteOperation | ||||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | 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 timber.log.Timber | ||||||
| import java.lang.reflect.Type |  | ||||||
| import java.net.URL | import java.net.URL | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -52,14 +46,10 @@ import java.net.URL | |||||||
|  * @author David A. Velasco |  * @author David A. Velasco | ||||||
|  * @author David González Verdugo |  * @author David González Verdugo | ||||||
|  * @author Fernando Sanz Velasco |  * @author Fernando Sanz Velasco | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Constructor |  | ||||||
|  * |  * | ||||||
|  * @param remoteShareId Share ID |  * @param remoteShareId Share ID | ||||||
|  */ |  */ | ||||||
| class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation<ShareResponse>() { | class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation<Unit>() { | ||||||
| 
 | 
 | ||||||
|     private fun buildRequestUri(baseUri: Uri) = |     private fun buildRequestUri(baseUri: Uri) = | ||||||
|         baseUri.buildUpon() |         baseUri.buildUpon() | ||||||
| @ -68,24 +58,12 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper | |||||||
|             .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) |             .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) | ||||||
|             .build() |             .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<CommonOcsResponse<List<ShareItem>>> = moshi.adapter(commonOcsType) |  | ||||||
|         return adapter.fromJson(response)?.ocs?.data?.let { listOfShareItems -> |  | ||||||
|             ShareResponse(listOfShareItems.map { shareItem -> |  | ||||||
|                 shareItem.toRemoteShare() |  | ||||||
|             }) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private fun onResultUnsuccessful( |     private fun onResultUnsuccessful( | ||||||
|         method: DeleteMethod, |         method: DeleteMethod, | ||||||
|         response: String?, |         response: String?, | ||||||
|         status: Int |         status: Int | ||||||
|     ): RemoteOperationResult<ShareResponse> { |     ): RemoteOperationResult<Unit> { | ||||||
|         Timber.e("Failed response while unshare link ") |         Timber.e("Failed response while removing share ") | ||||||
|         if (response != null) { |         if (response != null) { | ||||||
|             Timber.e("*** status code: $status; response message: $response") |             Timber.e("*** status code: $status; response message: $response") | ||||||
|         } else { |         } else { | ||||||
| @ -94,17 +72,14 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper | |||||||
|         return RemoteOperationResult(method) |         return RemoteOperationResult(method) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareResponse> { |     private fun onRequestSuccessful(response: String?): RemoteOperationResult<Unit> { | ||||||
|         val result = RemoteOperationResult<ShareResponse>(RemoteOperationResult.ResultCode.OK) |         val result = RemoteOperationResult<Unit>(RemoteOperationResult.ResultCode.OK) | ||||||
|         Timber.d("Successful response: $response") |         Timber.d("Successful response: $response") | ||||||
|         result.data = parseResponse(response!!) |  | ||||||
|         Timber.d("*** Unshare link completed ") |         Timber.d("*** Unshare link completed ") | ||||||
|         return result |         return result | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> { |     override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> { | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         val requestUri = buildRequestUri(client.baseUri) |         val requestUri = buildRequestUri(client.baseUri) | ||||||
| 
 | 
 | ||||||
|         val deleteMethod = DeleteMethod(URL(requestUri.toString())).apply { |         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 |     private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK | ||||||
| 
 | 
 | ||||||
|     companion object { |     companion object { | ||||||
|         //OCS Route |         // OCS Route | ||||||
|         private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/shares" |         private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/shares" | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -103,13 +103,6 @@ class UpdateRemoteShareOperation | |||||||
|      */ |      */ | ||||||
|     var permissions: Int = DEFAULT_PERMISSION |     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 |     var retrieveShareDetails = false // To retrieve more info about the just updated share | ||||||
| 
 | 
 | ||||||
|     private fun buildRequestUri(baseUri: Uri) = |     private fun buildRequestUri(baseUri: Uri) = | ||||||
| @ -119,7 +112,7 @@ class UpdateRemoteShareOperation | |||||||
|             .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) |             .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) | ||||||
|             .build() |             .build() | ||||||
| 
 | 
 | ||||||
|     private fun parseResponse(response: String): ShareResponse? { |     private fun parseResponse(response: String): ShareResponse { | ||||||
|         val moshi = Moshi.Builder().build() |         val moshi = Moshi.Builder().build() | ||||||
|         val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareItem::class.java) |         val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareItem::class.java) | ||||||
|         val adapter: JsonAdapter<CommonOcsResponse<ShareItem>> = moshi.adapter(commonOcsType) |         val adapter: JsonAdapter<CommonOcsResponse<ShareItem>> = moshi.adapter(commonOcsType) | ||||||
| @ -181,10 +174,6 @@ class UpdateRemoteShareOperation | |||||||
|             formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate) |             formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate) | ||||||
|         } // else, ignore - no update |         } // else, ignore - no update | ||||||
| 
 | 
 | ||||||
|         if (publicUpload != null) { |  | ||||||
|             formBodyBuilder.add(PARAM_PUBLIC_UPLOAD, publicUpload.toString()) |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // IMPORTANT: permissions parameter needs to be updated after mPublicUpload parameter, |         // 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 |         // otherwise they would be set always as 1 (READ) in the server when mPublicUpload was updated | ||||||
|         if (permissions > DEFAULT_PERMISSION) { |         if (permissions > DEFAULT_PERMISSION) { | ||||||
| @ -233,7 +222,6 @@ class UpdateRemoteShareOperation | |||||||
|         private const val PARAM_PASSWORD = "password" |         private const val PARAM_PASSWORD = "password" | ||||||
|         private const val PARAM_EXPIRATION_DATE = "expireDate" |         private const val PARAM_EXPIRATION_DATE = "expireDate" | ||||||
|         private const val PARAM_PERMISSIONS = "permissions" |         private const val PARAM_PERMISSIONS = "permissions" | ||||||
|         private const val PARAM_PUBLIC_UPLOAD = "publicUpload" |  | ||||||
| 
 | 
 | ||||||
|         //Arguments - constant values |         //Arguments - constant values | ||||||
|         private const val FORMAT_EXPIRATION_DATE = "yyyy-MM-dd" |         private const val FORMAT_EXPIRATION_DATE = "yyyy-MM-dd" | ||||||
|  | |||||||
| @ -46,7 +46,6 @@ interface ShareService : Service { | |||||||
|         name: String, |         name: String, | ||||||
|         password: String, |         password: String, | ||||||
|         expirationDate: Long, |         expirationDate: Long, | ||||||
|         publicUpload: Boolean |  | ||||||
|     ): RemoteOperationResult<ShareResponse> |     ): RemoteOperationResult<ShareResponse> | ||||||
| 
 | 
 | ||||||
|     fun updateShare( |     fun updateShare( | ||||||
| @ -55,8 +54,7 @@ interface ShareService : Service { | |||||||
|         password: String?, |         password: String?, | ||||||
|         expirationDate: Long, |         expirationDate: Long, | ||||||
|         permissions: Int, |         permissions: Int, | ||||||
|         publicUpload: Boolean |  | ||||||
|     ): RemoteOperationResult<ShareResponse> |     ): RemoteOperationResult<ShareResponse> | ||||||
| 
 | 
 | ||||||
|     fun deleteShare(remoteId: String): RemoteOperationResult<ShareResponse> |     fun deleteShare(remoteId: String): RemoteOperationResult<Unit> | ||||||
| } | } | ||||||
|  | |||||||
| @ -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.UpdateRemoteShareOperation | ||||||
| import com.owncloud.android.lib.resources.shares.services.ShareService | import com.owncloud.android.lib.resources.shares.services.ShareService | ||||||
| 
 | 
 | ||||||
| class OCShareService(override val client: OwnCloudClient) : | class OCShareService(override val client: OwnCloudClient) : ShareService { | ||||||
|     ShareService { |  | ||||||
|     override fun getShares( |     override fun getShares( | ||||||
|         remoteFilePath: String, |         remoteFilePath: String, | ||||||
|         reshares: Boolean, |         reshares: Boolean, | ||||||
| @ -56,7 +55,6 @@ class OCShareService(override val client: OwnCloudClient) : | |||||||
|         name: String, |         name: String, | ||||||
|         password: String, |         password: String, | ||||||
|         expirationDate: Long, |         expirationDate: Long, | ||||||
|         publicUpload: Boolean |  | ||||||
|     ): RemoteOperationResult<ShareResponse> = |     ): RemoteOperationResult<ShareResponse> = | ||||||
|         CreateRemoteShareOperation( |         CreateRemoteShareOperation( | ||||||
|             remoteFilePath, |             remoteFilePath, | ||||||
| @ -67,7 +65,6 @@ class OCShareService(override val client: OwnCloudClient) : | |||||||
|             this.name = name |             this.name = name | ||||||
|             this.password = password |             this.password = password | ||||||
|             this.expirationDateInMillis = expirationDate |             this.expirationDateInMillis = expirationDate | ||||||
|             this.publicUpload = publicUpload |  | ||||||
|             this.retrieveShareDetails = true |             this.retrieveShareDetails = true | ||||||
|         }.execute(client) |         }.execute(client) | ||||||
| 
 | 
 | ||||||
| @ -77,7 +74,6 @@ class OCShareService(override val client: OwnCloudClient) : | |||||||
|         password: String?, |         password: String?, | ||||||
|         expirationDate: Long, |         expirationDate: Long, | ||||||
|         permissions: Int, |         permissions: Int, | ||||||
|         publicUpload: Boolean |  | ||||||
|     ): RemoteOperationResult<ShareResponse> = |     ): RemoteOperationResult<ShareResponse> = | ||||||
|         UpdateRemoteShareOperation( |         UpdateRemoteShareOperation( | ||||||
|             remoteId |             remoteId | ||||||
| @ -86,11 +82,10 @@ class OCShareService(override val client: OwnCloudClient) : | |||||||
|             this.password = password |             this.password = password | ||||||
|             this.expirationDateInMillis = expirationDate |             this.expirationDateInMillis = expirationDate | ||||||
|             this.permissions = permissions |             this.permissions = permissions | ||||||
|             this.publicUpload = publicUpload |  | ||||||
|             this.retrieveShareDetails = true |             this.retrieveShareDetails = true | ||||||
|         }.execute(client) |         }.execute(client) | ||||||
| 
 | 
 | ||||||
|     override fun deleteShare(remoteId: String): RemoteOperationResult<ShareResponse> = |     override fun deleteShare(remoteId: String): RemoteOperationResult<Unit> = | ||||||
|         RemoveRemoteShareOperation( |         RemoveRemoteShareOperation( | ||||||
|             remoteId |             remoteId | ||||||
|         ).execute(client) |         ).execute(client) | ||||||
|  | |||||||
| @ -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<List<SpaceResponse>>() { | ||||||
|  |     override fun run(client: OwnCloudClient): RemoteOperationResult<List<SpaceResponse>> { | ||||||
|  |         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<SpaceResponse> { | ||||||
|  |         val moshi = Moshi.Builder().build() | ||||||
|  |         val adapter: JsonAdapter<SpacesResponseWrapper> = moshi.adapter(SpacesResponseWrapper::class.java) | ||||||
|  |         return adapter.fromJson(response)?.value ?: listOf() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun onResultUnsuccessful( | ||||||
|  |         method: GetMethod, | ||||||
|  |         response: String?, | ||||||
|  |         status: Int | ||||||
|  |     ): RemoteOperationResult<List<SpaceResponse>> { | ||||||
|  |         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<List<SpaceResponse>> { | ||||||
|  |         val result = RemoteOperationResult<List<SpaceResponse>>(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" | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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<SpaceResponse> | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | @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<SpecialResponse>?, | ||||||
|  |     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 | ||||||
|  | ) | ||||||
| @ -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<List<SpaceResponse>> { | ||||||
|  |         return GetRemoteSpacesOperation().execute(client) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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<List<SpaceResponse>> | ||||||
|  | } | ||||||
| @ -45,7 +45,7 @@ import java.net.URL | |||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Get the Capabilities from the server |  * Get the Capabilities from the server | ||||||
|  * Save in Result.getData in a RemoteCapability object |  * Save Result.getData in a RemoteCapability object | ||||||
|  * |  * | ||||||
|  * @author masensio |  * @author masensio | ||||||
|  * @author David González Verdugo |  * @author David González Verdugo | ||||||
|  | |||||||
| @ -1,8 +1,12 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /** | ||||||
|  |  * ownCloud Android Library is available under MIT license | ||||||
|  |  * | ||||||
|  * @author masensio |  * @author masensio | ||||||
|  * @author David González Verdugo |  * @author David González Verdugo | ||||||
|  * @author Abel García de Prada |  * @author Abel García de Prada | ||||||
|  *   Copyright (C) 2020 ownCloud GmbH. |  * @author Juan Carlos Garrote Gascón | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2022 ownCloud GmbH. | ||||||
|  * |  * | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  * of this software and associated documentation files (the "Software"), to deal | ||||||
| @ -22,8 +26,8 @@ | |||||||
|  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||||
|  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  * |  | ||||||
|  */ |  */ | ||||||
|  | 
 | ||||||
| package com.owncloud.android.lib.resources.status | package com.owncloud.android.lib.resources.status | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -33,7 +37,7 @@ data class RemoteCapability( | |||||||
|     var accountName: String = "", |     var accountName: String = "", | ||||||
| 
 | 
 | ||||||
|     // Server version |     // Server version | ||||||
|     var versionMayor: Int = 0, |     var versionMajor: Int = 0, | ||||||
|     var versionMinor: Int = 0, |     var versionMinor: Int = 0, | ||||||
|     var versionMicro: Int = 0, |     var versionMicro: Int = 0, | ||||||
|     var versionString: String = "", |     var versionString: String = "", | ||||||
| @ -68,7 +72,10 @@ data class RemoteCapability( | |||||||
|     var filesUndelete: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, |     var filesUndelete: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, | ||||||
|     var filesVersioning: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, |     var filesVersioning: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, | ||||||
|     val filesPrivateLinks: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, |     val filesPrivateLinks: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, | ||||||
|     val filesAppProviders: List<RemoteOCISProvider>?, |     val filesAppProviders: List<RemoteAppProviders>?, | ||||||
|  | 
 | ||||||
|  |     // Spaces | ||||||
|  |     val spaces: RemoteSpaces?, | ||||||
| ) { | ) { | ||||||
|     /** |     /** | ||||||
|      * Enum for Boolean Type in capabilities, with values: |      * 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 enabled: Boolean, | ||||||
|         val version: String, |         val version: String, | ||||||
|         val appsUrl: String?, |         val appsUrl: String?, | ||||||
| @ -109,4 +116,10 @@ data class RemoteCapability( | |||||||
|         val openWebUrl: String?, |         val openWebUrl: String?, | ||||||
|         val newUrl: String?, |         val newUrl: String?, | ||||||
|     ) |     ) | ||||||
|  | 
 | ||||||
|  |     data class RemoteSpaces( | ||||||
|  |         val enabled: Boolean, | ||||||
|  |         val projects: Boolean, | ||||||
|  |         val shareJail: Boolean, | ||||||
|  |     ) | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,6 +1,10 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /** | ||||||
|  |  * ownCloud Android Library is available under MIT license | ||||||
|  |  * | ||||||
|  * @author Abel García de Prada |  * @author Abel García de Prada | ||||||
|  *   Copyright (C) 2020 ownCloud GmbH. |  * @author Juan Carlos Garrote Gascón | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2022 ownCloud GmbH. | ||||||
|  * |  * | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  * of this software and associated documentation files (the "Software"), to deal | ||||||
| @ -20,13 +24,12 @@ | |||||||
|  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||||
|  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  * |  | ||||||
|  */ |  */ | ||||||
|  | 
 | ||||||
| package com.owncloud.android.lib.resources.status.responses | package com.owncloud.android.lib.resources.status.responses | ||||||
| 
 | 
 | ||||||
| import com.owncloud.android.lib.resources.status.RemoteCapability | 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.* | ||||||
| import com.owncloud.android.lib.resources.status.RemoteCapability.RemoteOCISProvider |  | ||||||
| import com.squareup.moshi.Json | import com.squareup.moshi.Json | ||||||
| import com.squareup.moshi.JsonClass | import com.squareup.moshi.JsonClass | ||||||
| 
 | 
 | ||||||
| @ -37,7 +40,7 @@ data class CapabilityResponse( | |||||||
|     val capabilities: Capabilities? |     val capabilities: Capabilities? | ||||||
| ) { | ) { | ||||||
|     fun toRemoteCapability(): RemoteCapability = RemoteCapability( |     fun toRemoteCapability(): RemoteCapability = RemoteCapability( | ||||||
|         versionMayor = serverVersion?.major ?: 0, |         versionMajor = serverVersion?.major ?: 0, | ||||||
|         versionMinor = serverVersion?.minor ?: 0, |         versionMinor = serverVersion?.minor ?: 0, | ||||||
|         versionMicro = serverVersion?.micro ?: 0, |         versionMicro = serverVersion?.micro ?: 0, | ||||||
|         versionString = serverVersion?.string ?: "", |         versionString = serverVersion?.string ?: "", | ||||||
| @ -70,10 +73,11 @@ data class CapabilityResponse( | |||||||
|         filesUndelete = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.undelete), |         filesUndelete = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.undelete), | ||||||
|         filesVersioning = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.versioning), |         filesVersioning = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.versioning), | ||||||
|         filesPrivateLinks = capabilities?.fileCapabilities?.privateLinks?.let { CapabilityBooleanType.fromBooleanValue(it) } ?: CapabilityBooleanType.UNKNOWN, |         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), |         filesSharingFederationIncoming = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.incoming), | ||||||
|         filesSharingFederationOutgoing = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.outgoing), |         filesSharingFederationOutgoing = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.outgoing), | ||||||
|         filesSharingUserProfilePicture = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingUser?.profilePicture), |         filesSharingUserProfilePicture = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingUser?.profilePicture), | ||||||
|  |         spaces = capabilities?.spacesCapabilities?.toSpaces(), | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -86,7 +90,9 @@ data class Capabilities( | |||||||
|     @Json(name = "files") |     @Json(name = "files") | ||||||
|     val fileCapabilities: FileCapabilities?, |     val fileCapabilities: FileCapabilities?, | ||||||
|     @Json(name = "dav") |     @Json(name = "dav") | ||||||
|     val davCapabilities: DavCapabilities? |     val davCapabilities: DavCapabilities?, | ||||||
|  |     @Json(name = "spaces") | ||||||
|  |     val spacesCapabilities: SpacesCapabilities?, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @JsonClass(generateAdapter = true) | @JsonClass(generateAdapter = true) | ||||||
| @ -182,7 +188,7 @@ data class AppProvider( | |||||||
|     @Json(name = "new_url") |     @Json(name = "new_url") | ||||||
|     val newUrl: String?, |     val newUrl: String?, | ||||||
| ) { | ) { | ||||||
|     fun toOCISProvider() = RemoteOCISProvider(enabled, version, appsUrl, openUrl, openWebUrl, newUrl) |     fun toAppProviders() = RemoteAppProviders(enabled, version, appsUrl, openUrl, openWebUrl, newUrl) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @JsonClass(generateAdapter = true) | @JsonClass(generateAdapter = true) | ||||||
| @ -190,6 +196,16 @@ data class DavCapabilities( | |||||||
|     val chunking: String? |     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) | @JsonClass(generateAdapter = true) | ||||||
| data class ServerVersion( | data class ServerVersion( | ||||||
|     var major: Int?, |     var major: Int?, | ||||||
|  | |||||||
| @ -1,8 +1,6 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /* ownCloud Android Library is available under MIT license | ||||||
|  *   Copyright (C) 2022 ownCloud GmbH. |  *   Copyright (C) 2022 ownCloud GmbH. | ||||||
|  * |  * | ||||||
|  *   @author David González Verdugo |  | ||||||
|  *  |  | ||||||
|  *   Permission is hereby granted, free of charge, to any person obtaining a copy |  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  *   of this software and associated documentation files (the "Software"), to deal |  *   of this software and associated documentation files (the "Software"), to deal | ||||||
|  *   in the Software without restriction, including without limitation the rights |  *   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.RemoteCapability | ||||||
| import com.owncloud.android.lib.resources.status.services.CapabilityService | import com.owncloud.android.lib.resources.status.services.CapabilityService | ||||||
| 
 | 
 | ||||||
| class OCCapabilityService(override val client: OwnCloudClient) : | class OCCapabilityService(override val client: OwnCloudClient) : CapabilityService { | ||||||
|     CapabilityService { |  | ||||||
|     override fun getCapabilities(): RemoteOperationResult<RemoteCapability> = |     override fun getCapabilities(): RemoteOperationResult<RemoteCapability> = | ||||||
|         GetRemoteCapabilitiesOperation().execute(client) |         GetRemoteCapabilitiesOperation().execute(client) | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,8 +1,6 @@ | |||||||
| /* ownCloud Android Library is available under MIT license | /* ownCloud Android Library is available under MIT license | ||||||
|  *   Copyright (C) 2022 ownCloud GmbH. |  *   Copyright (C) 2022 ownCloud GmbH. | ||||||
|  * |  * | ||||||
|  * @author Abel García de Prada  |  | ||||||
|  * |  | ||||||
|  *   Permission is hereby granted, free of charge, to any person obtaining a copy |  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  *   of this software and associated documentation files (the "Software"), to deal |  *   of this software and associated documentation files (the "Software"), to deal | ||||||
|  *   in the Software without restriction, including without limitation the rights |  *   in the Software without restriction, including without limitation the rights | ||||||
| @ -26,7 +24,6 @@ | |||||||
| 
 | 
 | ||||||
| package com.owncloud.android.lib.resources.status.services.implementation | package com.owncloud.android.lib.resources.status.services.implementation | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| import com.owncloud.android.lib.common.OwnCloudClient | import com.owncloud.android.lib.common.OwnCloudClient | ||||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||||
| import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation | import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation | ||||||
| @ -39,16 +36,16 @@ class OCServerInfoService : ServerInfoService { | |||||||
|     override fun checkPathExistence( |     override fun checkPathExistence( | ||||||
|         path: String, |         path: String, | ||||||
|         isUserLoggedIn: Boolean, |         isUserLoggedIn: Boolean, | ||||||
|         client: OwnCloudClient |         client: OwnCloudClient, | ||||||
|     ): RemoteOperationResult<Boolean> = |     ): RemoteOperationResult<Boolean> = | ||||||
|         CheckPathExistenceRemoteOperation( |         CheckPathExistenceRemoteOperation( | ||||||
|             remotePath = path, |             remotePath = path, | ||||||
|             isUserLoggedIn = true |             isUserLoggedIn = true, | ||||||
|         ).execute(client) |         ).execute(client) | ||||||
| 
 | 
 | ||||||
|     override fun getRemoteStatus( |     override fun getRemoteStatus( | ||||||
|         path: String, |         path: String, | ||||||
|         client: OwnCloudClient |         client: OwnCloudClient, | ||||||
|     ): RemoteOperationResult<RemoteServerInfo> = |     ): RemoteOperationResult<RemoteServerInfo> = | ||||||
|         GetRemoteStatusOperation().execute(client) |         GetRemoteStatusOperation().execute(client) | ||||||
| } | } | ||||||
|  | |||||||
| @ -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.http.methods.nonwebdav.HttpMethod | ||||||
| import com.owncloud.android.lib.common.operations.RemoteOperation | import com.owncloud.android.lib.common.operations.RemoteOperation | ||||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | 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 com.squareup.moshi.Moshi | ||||||
| import timber.log.Timber | import timber.log.Timber | ||||||
| import java.net.URL | import java.net.URL | ||||||
| 
 | 
 | ||||||
| class GetOCInstanceViaWebfingerOperation( | class GetInstancesViaWebFingerOperation( | ||||||
|     private val lockupServerDomain: String, |     private val lockupServerDomain: String, | ||||||
|     private val rel: String, |     private val rel: String, | ||||||
|     private val resource: String, |     private val resource: String, | ||||||
| ) : RemoteOperation<String>() { | ) : RemoteOperation<List<String>>() { | ||||||
| 
 | 
 | ||||||
|     private fun buildRequestUri() = |     private fun buildRequestUri() = | ||||||
|         Uri.parse(lockupServerDomain).buildUpon() |         Uri.parse(lockupServerDomain).buildUpon() | ||||||
| @ -51,9 +51,9 @@ class GetOCInstanceViaWebfingerOperation( | |||||||
| 
 | 
 | ||||||
|     private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK |     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 moshi = Moshi.Builder().build() | ||||||
|         val adapter = moshi.adapter(WebfingerJrdResponse::class.java) |         val adapter = moshi.adapter(WebFingerResponse::class.java) | ||||||
|         return adapter.fromJson(response)!! |         return adapter.fromJson(response)!! | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -61,32 +61,31 @@ class GetOCInstanceViaWebfingerOperation( | |||||||
|         method: HttpMethod, |         method: HttpMethod, | ||||||
|         response: String?, |         response: String?, | ||||||
|         status: Int |         status: Int | ||||||
|     ): RemoteOperationResult<String> { |     ): RemoteOperationResult<List<String>> { | ||||||
|         Timber.e("Failed requesting webfinger info") |         Timber.e("Failed requesting WebFinger info") | ||||||
|         if (response != null) { |         if (response != null) { | ||||||
|             Timber.e("*** status code: $status; response message: $response") |             Timber.e("*** status code: $status; response message: $response") | ||||||
|         } else { |         } else { | ||||||
|             Timber.e("*** status code: $status") |             Timber.e("*** status code: $status") | ||||||
|         } |         } | ||||||
|         return RemoteOperationResult<String>(method) |         return RemoteOperationResult<List<String>>(method) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun onRequestSuccessful(rawResponse: String): RemoteOperationResult<String> { |     private fun onRequestSuccessful(rawResponse: String): RemoteOperationResult<List<String>> { | ||||||
|         val response = parseResponse(rawResponse) |         val response = parseResponse(rawResponse) | ||||||
|         for (i in response.links) { |         Timber.d("Successful WebFinger request: $response") | ||||||
|             if (i.rel == rel) { |         val operationResult = RemoteOperationResult<List<String>>(RemoteOperationResult.ResultCode.OK) | ||||||
|                 val operationResult = RemoteOperationResult<String>(RemoteOperationResult.ResultCode.OK) |         operationResult.data = response.links?.map { it.href } ?: listOf() | ||||||
|                 operationResult.data = i.href |  | ||||||
|         return operationResult |         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") |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<String> { |     override fun run(client: OwnCloudClient): RemoteOperationResult<List<String>> { | ||||||
|         val requestUri = buildRequestUri() |         val requestUri = buildRequestUri() | ||||||
|         val getMethod = GetMethod(URL(requestUri.toString())) |         val getMethod = GetMethod(URL(requestUri.toString())) | ||||||
|  | 
 | ||||||
|  |         // First iteration won't follow redirections. | ||||||
|  |         getMethod.followRedirects = false | ||||||
|  | 
 | ||||||
|         return try { |         return try { | ||||||
|             val status = client.executeHttpMethod(getMethod) |             val status = client.executeHttpMethod(getMethod) | ||||||
|             val response = getMethod.getResponseBodyAsString()!! |             val response = getMethod.getResponseBodyAsString()!! | ||||||
| @ -97,8 +96,8 @@ class GetOCInstanceViaWebfingerOperation( | |||||||
|                 onResultUnsuccessful(getMethod, response, status) |                 onResultUnsuccessful(getMethod, response, status) | ||||||
|             } |             } | ||||||
|         } catch (e: Exception) { |         } catch (e: Exception) { | ||||||
|             Timber.e(e, "Requesting webfinger info failed") |             Timber.e(e, "Requesting WebFinger info failed") | ||||||
|             RemoteOperationResult<String>(e) |             RemoteOperationResult<List<String>>(e) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -27,13 +27,13 @@ package com.owncloud.android.lib.resources.webfinger.responses | |||||||
| import com.squareup.moshi.JsonClass | import com.squareup.moshi.JsonClass | ||||||
| 
 | 
 | ||||||
| @JsonClass(generateAdapter = true) | @JsonClass(generateAdapter = true) | ||||||
| data class WebfingerJrdResponse( | data class WebFingerResponse( | ||||||
|     val subject: String, |     val subject: String, | ||||||
|     val links: List<LinkItem> |     val links: List<LinkItem>? | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @JsonClass(generateAdapter = true) | @JsonClass(generateAdapter = true) | ||||||
| data class LinkItem( | data class LinkItem( | ||||||
|  |     val rel: String, | ||||||
|     val href: String, |     val href: String, | ||||||
|     val rel: String |  | ||||||
| ) | ) | ||||||
| @ -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.OwnCloudClient | ||||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||||
| 
 | 
 | ||||||
| interface WebfingerService { | interface WebFingerService { | ||||||
|     fun getInstanceFromWebfinger( |     fun getInstancesFromWebFinger( | ||||||
|         lookupServer: String, |         lookupServer: String, | ||||||
|         username: String, |         resource: String, | ||||||
|  |         rel: String, | ||||||
|         client: OwnCloudClient, |         client: OwnCloudClient, | ||||||
|     ): RemoteOperationResult<String> |     ): RemoteOperationResult<List<String>> | ||||||
| } | } | ||||||
| @ -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.OwnCloudClient | ||||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||||
| import com.owncloud.android.lib.resources.webfinger.GetOCInstanceViaWebfingerOperation | import com.owncloud.android.lib.resources.webfinger.GetInstancesViaWebFingerOperation | ||||||
| import com.owncloud.android.lib.resources.webfinger.services.WebfingerService | import com.owncloud.android.lib.resources.webfinger.services.WebFingerService | ||||||
| 
 | 
 | ||||||
| class OCWebfingerService : WebfingerService { | class OCWebFingerService : WebFingerService { | ||||||
| 
 | 
 | ||||||
|     override fun getInstanceFromWebfinger( |     override fun getInstancesFromWebFinger( | ||||||
|         lookupServer: String, |         lookupServer: String, | ||||||
|         username: String, |         resource: String, | ||||||
|  |         rel: String, | ||||||
|         client: OwnCloudClient, |         client: OwnCloudClient, | ||||||
|     ): RemoteOperationResult<String> = |     ): RemoteOperationResult<List<String>> = | ||||||
|         GetOCInstanceViaWebfingerOperation(lookupServer, OWNCLOUD_REL, username).execute(client) |         GetInstancesViaWebFingerOperation(lookupServer, rel, resource).execute(client) | ||||||
| 
 |  | ||||||
|     companion object { |  | ||||||
|         private const val OWNCLOUD_REL = "http://webfinger.owncloud/rel/server-instance" |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
| @ -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) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -8,34 +8,34 @@ import org.junit.Before | |||||||
| import org.junit.Test | import org.junit.Test | ||||||
| import java.io.File | import java.io.File | ||||||
| 
 | 
 | ||||||
| class WebfingerResponseTest { | class WebFingerResponseTest { | ||||||
|     lateinit var adapter: JsonAdapter<WebfingerJrdResponse> |     lateinit var adapter: JsonAdapter<WebFingerResponse> | ||||||
| 
 | 
 | ||||||
|     private fun loadResponses(fileName: String) = adapter.fromJson(File(fileName).readText()) |     private fun loadResponses(fileName: String) = adapter.fromJson(File(fileName).readText()) | ||||||
| 
 | 
 | ||||||
|     @Before |     @Before | ||||||
|     fun prepare() { |     fun prepare() { | ||||||
|         val moshi = Moshi.Builder().build() |         val moshi = Moshi.Builder().build() | ||||||
|         adapter = moshi.adapter(WebfingerJrdResponse::class.java) |         adapter = moshi.adapter(WebFingerResponse::class.java) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
|     fun `check rel in too much information - ok`() { |     fun `check rel in too much information - ok`() { | ||||||
|         val response = loadResponses(TOO_MUCH_INFORMATION_JSON)!! |         val response = loadResponses(TOO_MUCH_INFORMATION_JSON)!! | ||||||
|         Assert.assertEquals("https://gast.somedomain.de", response.links[0].href) |         Assert.assertEquals("https://gast.somedomain.de", response.links!![0].href) | ||||||
|         Assert.assertEquals("http://webfinger.owncloud/rel/server-instance", response.links[0].rel) |         Assert.assertEquals("http://webfinger.owncloud/rel/server-instance", response.links!![0].rel) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test(expected = JsonDataException::class) |     @Test(expected = JsonDataException::class) | ||||||
|     fun `check key value pairs - ko - no href key`() { |     fun `check key value pairs - ko - no href key`() { | ||||||
|         val response = loadResponses(BROKEN_JSON)!! |         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) |     @Test(expected = JsonDataException::class) | ||||||
|     fun `check key value pairs - ko - no rel key`() { |     fun `check key value pairs - ko - no rel key`() { | ||||||
|         val response = loadResponses(BROKEN_JSON)!! |         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 { |     companion object { | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user