mirror of
				https://github.com/owncloud/android-library.git
				synced 2025-10-31 02:17:41 +00:00 
			
		
		
		
	
						commit
						8c2c76c747
					
				| @ -6,5 +6,5 @@ jobs: | ||||
|     name: "Validation" | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: gradle/wrapper-validation-action@v1 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| buildscript { | ||||
|     ext { | ||||
|         kotlinVersion = '1.6.10' | ||||
|         kotlinVersion = '1.6.21' | ||||
|         moshiVersion = "1.13.0" | ||||
|     } | ||||
| 
 | ||||
| @ -10,8 +10,8 @@ buildscript { | ||||
|         maven { url "https://plugins.gradle.org/m2/" } | ||||
|     } | ||||
|     dependencies { | ||||
|         classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.1" | ||||
|         classpath 'com.android.tools.build:gradle:7.0.4' | ||||
|         classpath "org.jlleitschuh.gradle:ktlint-gradle:10.3.0" | ||||
|         classpath 'com.android.tools.build:gradle:7.1.3' | ||||
|         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										15
									
								
								check_code_script.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										15
									
								
								check_code_script.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,15 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| check_license_in_file() { | ||||
|     if ! head -n 20 $FILE | grep -q "Permission is hereby granted, free of charge, to any person obtaining a copy" | ||||
|     then | ||||
|         echo "$FILE does not contain a current copyright header" | ||||
|     fi | ||||
| } | ||||
| 
 | ||||
| for FILE in $(find owncloudComLibrary/src -name "*.java" -o -name "*.kt") | ||||
| do | ||||
|     check_license_in_file | ||||
| done | ||||
| 
 | ||||
| ./gradlew ktlintFormat | ||||
| @ -12,33 +12,27 @@ dependencies { | ||||
|     implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") { | ||||
|         exclude module: "kotlin-reflect" | ||||
|     } | ||||
|     implementation 'org.apache.commons:commons-lang3:3.12.0' | ||||
|     kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" | ||||
| 
 | ||||
|     testImplementation 'junit:junit:4.13.2' | ||||
|     testImplementation 'org.robolectric:robolectric:4.7.3' | ||||
|     testImplementation 'org.robolectric:robolectric:4.8.1' | ||||
|     debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.6.0' | ||||
| } | ||||
| 
 | ||||
| android { | ||||
|     compileSdkVersion 30 | ||||
|     compileSdkVersion 31 | ||||
| 
 | ||||
|     defaultConfig { | ||||
|         minSdkVersion 21 | ||||
|         targetSdkVersion 30 | ||||
| 
 | ||||
|         versionCode = 10001400 | ||||
|         versionName = "1.0.14" | ||||
|         targetSdkVersion 31 | ||||
|     } | ||||
| 
 | ||||
|     lintOptions { | ||||
|     lint { | ||||
|         abortOnError false | ||||
|         ignoreWarnings true | ||||
|     } | ||||
| 
 | ||||
|     compileOptions { | ||||
|         sourceCompatibility JavaVersion.VERSION_1_8 | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
| 
 | ||||
|     testOptions { | ||||
|         unitTests { | ||||
|             includeAndroidResources = true | ||||
|  | ||||
| @ -19,40 +19,12 @@ | ||||
|  *   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.common; | ||||
| package com.owncloud.android.lib.common.http | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.net.Uri; | ||||
| import com.facebook.stetho.okhttp3.StethoInterceptor | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.http.HttpClient; | ||||
| import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation; | ||||
| 
 | ||||
| public class OwnCloudClientFactory { | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a OwnCloudClient to access a URL and sets the desired parameters for ownCloud | ||||
|      * client connections. | ||||
|      * | ||||
|      * @param uri     URL to the ownCloud server; BASE ENTRY POINT, not WebDavPATH | ||||
|      * @param context Android context where the OwnCloudClient is being created. | ||||
|      * @return A OwnCloudClient object ready to be used | ||||
|      */ | ||||
|     public static OwnCloudClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) { | ||||
|         OwnCloudClient client = new OwnCloudClient(uri); | ||||
| 
 | ||||
|         client.setFollowRedirects(followRedirects); | ||||
| 
 | ||||
|         HttpClient.setContext(context); | ||||
|         retrieveCookiesFromMiddleware(client); | ||||
| 
 | ||||
|         return client; | ||||
|     } | ||||
| 
 | ||||
|     private static void retrieveCookiesFromMiddleware(OwnCloudClient client) { | ||||
|         final GetRemoteStatusOperation statusOperation = new GetRemoteStatusOperation(); | ||||
|         statusOperation.run(client); | ||||
|     } | ||||
| object DebugInterceptorFactory { | ||||
|     fun getInterceptor() = StethoInterceptor() | ||||
| } | ||||
| @ -0,0 +1,215 @@ | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2016 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.common | ||||
| 
 | ||||
| import android.accounts.AccountManager | ||||
| import android.accounts.AccountsException | ||||
| import android.content.Context | ||||
| import com.owncloud.android.lib.common.authentication.OwnCloudCredentials | ||||
| import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials | ||||
| import com.owncloud.android.lib.common.http.HttpConstants | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation | ||||
| import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation | ||||
| import com.owncloud.android.lib.resources.status.RemoteServerInfo | ||||
| import org.apache.commons.lang3.exception.ExceptionUtils | ||||
| import timber.log.Timber | ||||
| import java.io.IOException | ||||
| import java.lang.Exception | ||||
| 
 | ||||
| /** | ||||
|  * ConnectionValidator | ||||
|  * | ||||
|  * @author Christian Schabesberger | ||||
|  */ | ||||
| class ConnectionValidator( | ||||
|     val context: Context, | ||||
|     val clearCookiesOnValidation: Boolean | ||||
| ) { | ||||
|     fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager, context: Context): Boolean { | ||||
|         try { | ||||
|             var validationRetryCount = 0 | ||||
|             val client = OwnCloudClient(baseClient.baseUri, null, false, singleSessionManager, context) | ||||
|             if (clearCookiesOnValidation) { | ||||
|                 client.clearCookies() | ||||
|             } else { | ||||
|                 client.cookiesForBaseUri = baseClient.cookiesForBaseUri | ||||
|             } | ||||
| 
 | ||||
|             client.account = baseClient.account | ||||
|             client.credentials = baseClient.credentials | ||||
|             while (validationRetryCount < VALIDATION_RETRY_COUNT) { | ||||
|                 Timber.d("validationRetryCout %d", validationRetryCount) | ||||
|                 var successCounter = 0 | ||||
|                 var failCounter = 0 | ||||
| 
 | ||||
|                 client.setFollowRedirects(true) | ||||
|                 if (isOnwCloudStatusOk(client)) { | ||||
|                     successCounter++ | ||||
|                 } else { | ||||
|                     failCounter++ | ||||
|                 } | ||||
| 
 | ||||
|                 // Skip the part where we try to check if we can access the parts where we have to be logged in... if we are not logged in | ||||
|                 if (baseClient.credentials !is OwnCloudAnonymousCredentials) { | ||||
|                     client.setFollowRedirects(false) | ||||
|                     val contentReply = canAccessRootFolder(client) | ||||
|                     if (contentReply.httpCode == HttpConstants.HTTP_OK) { | ||||
|                         if (contentReply.data == true) { //if data is true it means that the content reply was ok | ||||
|                             successCounter++ | ||||
|                         } else { | ||||
|                             failCounter++ | ||||
|                         } | ||||
|                     } else { | ||||
|                         failCounter++ | ||||
|                         if (contentReply.httpCode == HttpConstants.HTTP_UNAUTHORIZED) { | ||||
|                             checkUnauthorizedAccess(client, singleSessionManager, contentReply.httpCode) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (successCounter >= failCounter) { | ||||
|                     baseClient.credentials = client.credentials | ||||
|                     baseClient.cookiesForBaseUri = client.cookiesForBaseUri | ||||
|                     return true | ||||
|                 } | ||||
|                 validationRetryCount++ | ||||
|             } | ||||
|             Timber.d("Could not authenticate or get valid data from owncloud") | ||||
|         } catch (e: Exception) { | ||||
|             Timber.d(ExceptionUtils.getStackTrace(e)) | ||||
|         } | ||||
|         return false | ||||
|     } | ||||
| 
 | ||||
|     private fun isOnwCloudStatusOk(client: OwnCloudClient): Boolean { | ||||
|         val reply = getOwnCloudStatus(client) | ||||
|         // dont check status code. It currently relais on the broken redirect code of the owncloud client | ||||
|         // TODO: Use okhttp redirect and add this check again | ||||
|         // return reply.httpCode == HttpConstants.HTTP_OK && | ||||
|         return !reply.isException && | ||||
|                 reply.data != null | ||||
|     } | ||||
| 
 | ||||
|     private fun getOwnCloudStatus(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> { | ||||
|         val remoteStatusOperation = GetRemoteStatusOperation() | ||||
|         return remoteStatusOperation.execute(client) | ||||
|     } | ||||
| 
 | ||||
|     private fun canAccessRootFolder(client: OwnCloudClient): RemoteOperationResult<Boolean> { | ||||
|         val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation("/", true) | ||||
|         return checkPathExistenceRemoteOperation.execute(client) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Determines if credentials should be invalidated according the to the HTTPS status | ||||
|      * of a network request just performed. | ||||
|      * | ||||
|      * @param httpStatusCode Result of the last request ran with the 'credentials' belows. | ||||
|      * @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or | ||||
|      * cannot be invalidated with the given arguments. | ||||
|      */ | ||||
|     private fun shouldInvalidateAccountCredentials(credentials: OwnCloudCredentials, account: OwnCloudAccount, httpStatusCode: Int): Boolean { | ||||
|         var shouldInvalidateAccountCredentials = httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED | ||||
|         shouldInvalidateAccountCredentials = shouldInvalidateAccountCredentials and  // real credentials | ||||
|                 (credentials !is OwnCloudAnonymousCredentials) | ||||
| 
 | ||||
|         // test if have all the needed to effectively invalidate ... | ||||
|         shouldInvalidateAccountCredentials = | ||||
|             shouldInvalidateAccountCredentials and (account.savedAccount != null) | ||||
|         return shouldInvalidateAccountCredentials | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Invalidates credentials stored for the given account in the system  [AccountManager] and in | ||||
|      * current [SingleSessionManager.getDefaultSingleton] instance. | ||||
|      * | ||||
|      * | ||||
|      * [.shouldInvalidateAccountCredentials] should be called first. | ||||
|      * | ||||
|      */ | ||||
|     private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) { | ||||
|         val am = AccountManager.get(context) | ||||
|         am.invalidateAuthToken( | ||||
|             account.savedAccount.type, | ||||
|             credentials.authToken | ||||
|         ) | ||||
|         am.clearPassword(account.savedAccount) // being strict, only needed for Basic Auth credentials | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks the status code of an execution and decides if should be repeated with fresh credentials. | ||||
|      * | ||||
|      * | ||||
|      * Invalidates current credentials if the request failed as anauthorized. | ||||
|      * | ||||
|      * | ||||
|      * Refresh current credentials if possible, and marks a retry. | ||||
|      * | ||||
|      * @param status | ||||
|      * @param repeatCounter | ||||
|      * @return | ||||
|      */ | ||||
|     private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean { | ||||
|         var credentialsWereRefreshed = false | ||||
|         val account = client.account | ||||
|         val credentials = account.credentials | ||||
|         if (shouldInvalidateAccountCredentials(credentials, account, status)) { | ||||
|             invalidateAccountCredentials(account, credentials) | ||||
| 
 | ||||
|             if (credentials.authTokenCanBeRefreshed()) { | ||||
|                 try { | ||||
|                     // This command does the actual refresh | ||||
|                     account.loadCredentials(context) | ||||
|                     // if mAccount.getCredentials().length() == 0 --> refresh failed | ||||
|                     client.credentials = account.credentials | ||||
|                     credentialsWereRefreshed = true | ||||
|                 } catch (e: AccountsException) { | ||||
|                     Timber.e( | ||||
|                         e, "Error while trying to refresh auth token for %s\ntrace: %s", | ||||
|                         account.savedAccount.name, | ||||
|                         ExceptionUtils.getStackTrace(e) | ||||
|                     ) | ||||
|                 } catch (e: IOException) { | ||||
|                     Timber.e( | ||||
|                         e, "Error while trying to refresh auth token for %s\ntrace: %s", | ||||
|                         account.savedAccount.name, | ||||
|                         ExceptionUtils.getStackTrace(e) | ||||
|                     ) | ||||
|                 } | ||||
|                 if (!credentialsWereRefreshed) { | ||||
|                     // if credentials are not refreshed, client must be removed | ||||
|                     // from the OwnCloudClientManager to prevent it is reused once and again | ||||
|                     singleSessionManager.removeClientFor(account) | ||||
|                 } | ||||
|             } | ||||
|             // else: onExecute will finish with status 401 | ||||
|         } | ||||
|         return credentialsWereRefreshed | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         private val VALIDATION_RETRY_COUNT = 3 | ||||
|     } | ||||
| } | ||||
| @ -25,11 +25,9 @@ | ||||
| 
 | ||||
| package com.owncloud.android.lib.common; | ||||
| 
 | ||||
| import android.accounts.AccountManager; | ||||
| import android.accounts.AccountsException; | ||||
| import android.content.Context; | ||||
| import android.net.Uri; | ||||
| 
 | ||||
| import at.bitfire.dav4jvm.exception.HttpException; | ||||
| import com.owncloud.android.lib.common.accounts.AccountUtils; | ||||
| import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; | ||||
| import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; | ||||
| @ -37,7 +35,6 @@ import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory | ||||
| import com.owncloud.android.lib.common.http.HttpClient; | ||||
| import com.owncloud.android.lib.common.http.HttpConstants; | ||||
| import com.owncloud.android.lib.common.http.methods.HttpBaseMethod; | ||||
| import com.owncloud.android.lib.common.network.RedirectionPath; | ||||
| import com.owncloud.android.lib.common.utils.RandomUtils; | ||||
| import com.owncloud.android.lib.resources.status.OwnCloudVersion; | ||||
| import okhttp3.Cookie; | ||||
| @ -49,40 +46,54 @@ import java.io.InputStream; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER; | ||||
| import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID; | ||||
| import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY; | ||||
| 
 | ||||
| public class OwnCloudClient extends HttpClient { | ||||
| 
 | ||||
|     public static final String WEBDAV_FILES_PATH_4_0 = "/remote.php/dav/files/"; | ||||
|     public static final String WEBDAV_PATH_4_0_AND_LATER = "/remote.php/dav"; | ||||
|     public static final String STATUS_PATH = "/status.php"; | ||||
|     private static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/"; | ||||
|     private static final int MAX_REDIRECTIONS_COUNT = 3; | ||||
|     private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1; | ||||
|     private static final int MAX_RETRY_COUNT = 2; | ||||
| 
 | ||||
|     private static byte[] sExhaustBuffer = new byte[1024]; | ||||
|     private static int sIntanceCounter = 0; | ||||
|     private OwnCloudCredentials mCredentials = null; | ||||
|     private int mInstanceNumber; | ||||
|     private Uri mBaseUri; | ||||
|     private OwnCloudVersion mVersion = null; | ||||
|     private OwnCloudAccount mAccount; | ||||
|     private final ConnectionValidator mConnectionValidator; | ||||
|     private Object mRequestMutex = new Object(); | ||||
| 
 | ||||
|     // If set to true a mutex will be used to prevent parallel execution of the execute() method | ||||
|     // if false the execute() method can be called even though the mutex is already aquired. | ||||
|     // This is used for the ConnectionValidator, which has to be able to execute OperationsWhile all "normal" operations net | ||||
|     // to be set on hold. | ||||
|     private final Boolean mSynchronizeRequests; | ||||
| 
 | ||||
|     private SingleSessionManager mSingleSessionManager = null; | ||||
| 
 | ||||
|     private boolean mFollowRedirects; | ||||
|     private boolean mFollowRedirects = false; | ||||
| 
 | ||||
|     public OwnCloudClient(Uri baseUri, | ||||
|                           ConnectionValidator connectionValidator, | ||||
|                           boolean synchronizeRequests, | ||||
|                           SingleSessionManager singleSessionManager, | ||||
|                           Context context) { | ||||
|         super(context); | ||||
| 
 | ||||
|     public OwnCloudClient(Uri baseUri) { | ||||
|         if (baseUri == null) { | ||||
|             throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL"); | ||||
|         } | ||||
|         mBaseUri = baseUri; | ||||
|         mSynchronizeRequests = synchronizeRequests; | ||||
|         mSingleSessionManager = singleSessionManager; | ||||
| 
 | ||||
|         mInstanceNumber = sIntanceCounter++; | ||||
|         Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient"); | ||||
| 
 | ||||
|         clearCredentials(); | ||||
|         clearCookies(); | ||||
|         mConnectionValidator = connectionValidator; | ||||
|     } | ||||
| 
 | ||||
|     public void clearCredentials() { | ||||
| @ -92,11 +103,27 @@ public class OwnCloudClient extends HttpClient { | ||||
|     } | ||||
| 
 | ||||
|     public int executeHttpMethod(HttpBaseMethod method) throws Exception { | ||||
|         boolean repeatWithFreshCredentials; | ||||
|         if (mSynchronizeRequests) { | ||||
|             synchronized (mRequestMutex) { | ||||
|                 return saveExecuteHttpMethod(method); | ||||
|             } | ||||
|         } else { | ||||
|             return saveExecuteHttpMethod(method); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private int saveExecuteHttpMethod(HttpBaseMethod method) throws Exception { | ||||
|         int repeatCounter = 0; | ||||
|         int status; | ||||
| 
 | ||||
|         if (mFollowRedirects) { | ||||
|             method.setFollowRedirects(true); | ||||
|         } | ||||
| 
 | ||||
|         boolean retry; | ||||
|         do { | ||||
|             repeatCounter++; | ||||
|             retry = false; | ||||
|             String requestId = RandomUtils.generateRandomUUID(); | ||||
| 
 | ||||
|             // Header to allow tracing requests in apache and ownCloud logs | ||||
| @ -104,105 +131,34 @@ public class OwnCloudClient extends HttpClient { | ||||
|             method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId); | ||||
|             method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent()); | ||||
|             method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY); | ||||
|             if (mCredentials.getHeaderAuth() != null && method.getRequestHeader(AUTHORIZATION_HEADER) == null) { | ||||
|             if (mCredentials.getHeaderAuth() != null && !mCredentials.getHeaderAuth().isEmpty()) { | ||||
|                 method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); | ||||
|             } | ||||
|             status = method.execute(); | ||||
| 
 | ||||
|             if (mFollowRedirects) { | ||||
|                 status = followRedirection(method).getLastStatus(); | ||||
|             status = method.execute(this); | ||||
| 
 | ||||
|             if (shouldConnectionValidatorBeCalled(method, status)) { | ||||
|                 retry = mConnectionValidator.validate(this, mSingleSessionManager, getContext()); // retry on success fail on no success | ||||
|             } else if (method.getFollowPermanentRedirects() && status == HTTP_MOVED_PERMANENTLY) { | ||||
|                 retry = true; | ||||
|                 method.setFollowRedirects(true); | ||||
|             } | ||||
| 
 | ||||
|             repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter); | ||||
|             if (repeatWithFreshCredentials) { | ||||
|                 repeatCounter++; | ||||
|             } | ||||
|         } while (repeatWithFreshCredentials); | ||||
|         } while (retry && repeatCounter < MAX_RETRY_COUNT); | ||||
| 
 | ||||
|         return status; | ||||
|     } | ||||
| 
 | ||||
|     private int executeRedirectedHttpMethod(HttpBaseMethod method) throws Exception { | ||||
|         boolean repeatWithFreshCredentials; | ||||
|         int repeatCounter = 0; | ||||
|         int status; | ||||
|     private boolean shouldConnectionValidatorBeCalled(HttpBaseMethod method, int status) { | ||||
| 
 | ||||
|         do { | ||||
|             String requestId = RandomUtils.generateRandomUUID(); | ||||
| 
 | ||||
|             // Header to allow tracing requests in apache and ownCloud logs | ||||
|             Timber.d("Executing in request with id %s", requestId); | ||||
|             method.setRequestHeader(OC_X_REQUEST_ID, requestId); | ||||
|             method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent()); | ||||
|             method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY); | ||||
|             if (mCredentials.getHeaderAuth() != null) { | ||||
|                 method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); | ||||
|             } | ||||
|             status = method.execute(); | ||||
| 
 | ||||
|             repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter); | ||||
|             if (repeatWithFreshCredentials) { | ||||
|                 repeatCounter++; | ||||
|             } | ||||
|         } while (repeatWithFreshCredentials); | ||||
| 
 | ||||
|         return status; | ||||
|     } | ||||
| 
 | ||||
|     public RedirectionPath followRedirection(HttpBaseMethod method) throws Exception { | ||||
|         int redirectionsCount = 0; | ||||
|         int status = method.getStatusCode(); | ||||
|         RedirectionPath redirectionPath = new RedirectionPath(status, MAX_REDIRECTIONS_COUNT); | ||||
| 
 | ||||
|         while (redirectionsCount < MAX_REDIRECTIONS_COUNT && | ||||
|                 (status == HttpConstants.HTTP_MOVED_PERMANENTLY || | ||||
|                         status == HttpConstants.HTTP_MOVED_TEMPORARILY || | ||||
|                         status == HttpConstants.HTTP_TEMPORARY_REDIRECT) | ||||
|         ) { | ||||
| 
 | ||||
|             final String location = method.getResponseHeader(HttpConstants.LOCATION_HEADER) != null | ||||
|                     ? method.getResponseHeader(HttpConstants.LOCATION_HEADER) | ||||
|                     : method.getResponseHeader(HttpConstants.LOCATION_HEADER_LOWER); | ||||
| 
 | ||||
|             if (location != null) { | ||||
|                 Timber.d("#" + mInstanceNumber + "Location to redirect: " + location); | ||||
| 
 | ||||
|                 redirectionPath.addLocation(location); | ||||
| 
 | ||||
|                 // Release the connection to avoid reach the max number of connections per host | ||||
|                 // due to it will be set a different url | ||||
|                 exhaustResponse(method.getResponseBodyAsStream()); | ||||
| 
 | ||||
|                 method.setUrl(HttpUrl.parse(location)); | ||||
|                 final String destination = method.getRequestHeader("Destination") != null | ||||
|                         ? method.getRequestHeader("Destination") | ||||
|                         : method.getRequestHeader("destination"); | ||||
| 
 | ||||
|                 if (destination != null) { | ||||
|                     final int suffixIndex = location.lastIndexOf(getUserFilesWebDavUri().toString()); | ||||
|                     final String redirectionBase = location.substring(0, suffixIndex); | ||||
|                     final String destinationPath = destination.substring(mBaseUri.toString().length()); | ||||
| 
 | ||||
|                     method.setRequestHeader("destination", redirectionBase + destinationPath); | ||||
|                 } | ||||
|                 try { | ||||
|                     status = executeRedirectedHttpMethod(method); | ||||
|                 } catch (HttpException e) { | ||||
|                     if (e.getMessage().contains(Integer.toString(HttpConstants.HTTP_MOVED_TEMPORARILY))) { | ||||
|                         status = HttpConstants.HTTP_MOVED_TEMPORARILY; | ||||
|                     } else { | ||||
|                         throw e; | ||||
|                     } | ||||
|                 } | ||||
|                 redirectionPath.addStatus(status); | ||||
|                 redirectionsCount++; | ||||
| 
 | ||||
|             } else { | ||||
|                 Timber.d(" #" + mInstanceNumber + "No location to redirect!"); | ||||
|                 status = HttpConstants.HTTP_NOT_FOUND; | ||||
|             } | ||||
|         } | ||||
|         return redirectionPath; | ||||
|         return mConnectionValidator != null && ( | ||||
|                 (!(mCredentials instanceof OwnCloudAnonymousCredentials) && | ||||
|                         status == HttpConstants.HTTP_UNAUTHORIZED | ||||
|                 ) || (!mFollowRedirects && | ||||
|                         !method.getFollowRedirects() && | ||||
|                         status == HttpConstants.HTTP_MOVED_TEMPORARILY | ||||
|                 ) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -273,26 +229,18 @@ public class OwnCloudClient extends HttpClient { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public String getCookiesString() { | ||||
|         StringBuilder cookiesString = new StringBuilder(); | ||||
|         List<Cookie> cookieList = getCookiesFromUrl(HttpUrl.parse(mBaseUri.toString())); | ||||
| 
 | ||||
|         if (cookieList != null) { | ||||
|             for (Cookie cookie : cookieList) { | ||||
|                 cookiesString.append(cookie.toString()).append(";"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return cookiesString.toString(); | ||||
|     } | ||||
| 
 | ||||
|     public void setCookiesForCurrentAccount(List<Cookie> cookies) { | ||||
|     public void setCookiesForBaseUri(List<Cookie> cookies) { | ||||
|         getOkHttpClient().cookieJar().saveFromResponse( | ||||
|                 HttpUrl.parse(getAccount().getBaseUri().toString()), | ||||
|                 HttpUrl.parse(mBaseUri.toString()), | ||||
|                 cookies | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public List<Cookie> getCookiesForBaseUri() { | ||||
|         return getOkHttpClient().cookieJar().loadForRequest( | ||||
|                 HttpUrl.parse(mBaseUri.toString())); | ||||
|     } | ||||
| 
 | ||||
|     public OwnCloudVersion getOwnCloudVersion() { | ||||
|         return mVersion; | ||||
|     } | ||||
| @ -309,94 +257,6 @@ public class OwnCloudClient extends HttpClient { | ||||
|         this.mAccount = account; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks the status code of an execution and decides if should be repeated with fresh credentials. | ||||
|      * <p> | ||||
|      * Invalidates current credentials if the request failed as anauthorized. | ||||
|      * <p> | ||||
|      * Refresh current credentials if possible, and marks a retry. | ||||
|      * | ||||
|      * @param status | ||||
|      * @param repeatCounter | ||||
|      * @return | ||||
|      */ | ||||
|     private boolean checkUnauthorizedAccess(int status, int repeatCounter) { | ||||
|         boolean credentialsWereRefreshed = false; | ||||
| 
 | ||||
|         if (shouldInvalidateAccountCredentials(status)) { | ||||
|             boolean invalidated = invalidateAccountCredentials(); | ||||
| 
 | ||||
|             if (invalidated) { | ||||
|                 if (getCredentials().authTokenCanBeRefreshed() && | ||||
|                         repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) { | ||||
|                     try { | ||||
|                         mAccount.loadCredentials(getContext()); | ||||
|                         // if mAccount.getCredentials().length() == 0 --> refresh failed | ||||
|                         setCredentials(mAccount.getCredentials()); | ||||
|                         credentialsWereRefreshed = true; | ||||
| 
 | ||||
|                     } catch (AccountsException | IOException e) { | ||||
|                         Timber.e(e, "Error while trying to refresh auth token for %s", | ||||
|                                 mAccount.getSavedAccount().name | ||||
|                         ); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if (!credentialsWereRefreshed && mSingleSessionManager != null) { | ||||
|                     // if credentials are not refreshed, client must be removed | ||||
|                     // from the OwnCloudClientManager to prevent it is reused once and again | ||||
|                     mSingleSessionManager.removeClientFor(mAccount); | ||||
|                 } | ||||
|             } | ||||
|             // else: onExecute will finish with status 401 | ||||
|         } | ||||
| 
 | ||||
|         return credentialsWereRefreshed; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Determines if credentials should be invalidated according the to the HTTPS status | ||||
|      * of a network request just performed. | ||||
|      * | ||||
|      * @param httpStatusCode Result of the last request ran with the 'credentials' belows. | ||||
|      * @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or | ||||
|      * cannot be invalidated with the given arguments. | ||||
|      */ | ||||
|     private boolean shouldInvalidateAccountCredentials(int httpStatusCode) { | ||||
|         boolean shouldInvalidateAccountCredentials = | ||||
|                 (httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED); | ||||
| 
 | ||||
|         shouldInvalidateAccountCredentials &= (mCredentials != null &&         // real credentials | ||||
|                 !(mCredentials instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials)); | ||||
| 
 | ||||
|         // test if have all the needed to effectively invalidate ... | ||||
|         shouldInvalidateAccountCredentials &= (mAccount != null && mAccount.getSavedAccount() != null && getContext() != null); | ||||
| 
 | ||||
|         return shouldInvalidateAccountCredentials; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Invalidates credentials stored for the given account in the system  {@link AccountManager} and in | ||||
|      * current {@link SingleSessionManager#getDefaultSingleton()} instance. | ||||
|      * <p> | ||||
|      * {@link #shouldInvalidateAccountCredentials(int)} should be called first. | ||||
|      * | ||||
|      * @return 'True' if invalidation was successful, 'false' otherwise. | ||||
|      */ | ||||
|     private boolean invalidateAccountCredentials() { | ||||
|         AccountManager am = AccountManager.get(getContext()); | ||||
|         am.invalidateAuthToken( | ||||
|                 mAccount.getSavedAccount().type, | ||||
|                 mCredentials.getAuthToken() | ||||
|         ); | ||||
|         am.clearPassword(mAccount.getSavedAccount()); // being strict, only needed for Basic Auth credentials | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public boolean followRedirects() { | ||||
|         return mFollowRedirects; | ||||
|     } | ||||
| 
 | ||||
|     public void setFollowRedirects(boolean followRedirects) { | ||||
|         this.mFollowRedirects = followRedirects; | ||||
|     } | ||||
|  | ||||
| @ -49,6 +49,7 @@ public class SingleSessionManager { | ||||
| 
 | ||||
|     private static SingleSessionManager sDefaultSingleton; | ||||
|     private static String sUserAgent; | ||||
|     private static ConnectionValidator sConnectionValidator; | ||||
| 
 | ||||
|     private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername = new ConcurrentHashMap<>(); | ||||
|     private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername = new ConcurrentHashMap<>(); | ||||
| @ -60,6 +61,14 @@ public class SingleSessionManager { | ||||
|         return sDefaultSingleton; | ||||
|     } | ||||
| 
 | ||||
|     public static void setConnectionValidator(ConnectionValidator connectionValidator) { | ||||
|         sConnectionValidator = connectionValidator; | ||||
|     } | ||||
| 
 | ||||
|     public static ConnectionValidator getConnectionValidator() { | ||||
|         return sConnectionValidator; | ||||
|     } | ||||
| 
 | ||||
|     public static String getUserAgent() { | ||||
|         return sUserAgent; | ||||
|     } | ||||
| @ -68,7 +77,23 @@ public class SingleSessionManager { | ||||
|         sUserAgent = userAgent; | ||||
|     } | ||||
| 
 | ||||
|     public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws OperationCanceledException, | ||||
|     private static OwnCloudClient createOwnCloudClient(Uri uri, | ||||
|                                                        Context context, | ||||
|                                                        ConnectionValidator connectionValidator, | ||||
|                                                        SingleSessionManager singleSessionManager) { | ||||
|         OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true, singleSessionManager, context); | ||||
|         return client; | ||||
|     } | ||||
| 
 | ||||
|     public OwnCloudClient getClientFor(OwnCloudAccount account, | ||||
|                                        Context context) throws OperationCanceledException, | ||||
|             AuthenticatorException, IOException { | ||||
|         return getClientFor(account, context, getConnectionValidator()); | ||||
|     } | ||||
| 
 | ||||
|     public OwnCloudClient getClientFor(OwnCloudAccount account, | ||||
|                                        Context context, | ||||
|                                        ConnectionValidator connectionValidator) throws OperationCanceledException, | ||||
|             AuthenticatorException, IOException { | ||||
| 
 | ||||
|         Timber.d("getClientFor starting "); | ||||
| @ -104,10 +129,11 @@ public class SingleSessionManager { | ||||
| 
 | ||||
|         if (client == null) { | ||||
|             // no client to reuse - create a new one | ||||
|             client = OwnCloudClientFactory.createOwnCloudClient( | ||||
|             client = createOwnCloudClient( | ||||
|                     account.getBaseUri(), | ||||
|                     context.getApplicationContext(), | ||||
|                     true);    // TODO remove dependency on OwnCloudClientFactory | ||||
|                     context, | ||||
|                     connectionValidator, | ||||
|                     this); // TODO remove dependency on OwnCloudClientFactory | ||||
| 
 | ||||
|             //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 | ||||
| @ -115,7 +141,6 @@ public class SingleSessionManager { | ||||
|             client.clearCredentials(); | ||||
| 
 | ||||
|             client.setAccount(account); | ||||
|             HttpClient.setContext(context); | ||||
| 
 | ||||
|             account.loadCredentials(context); | ||||
|             client.setCredentials(account.getCredentials()); | ||||
|  | ||||
| @ -28,7 +28,7 @@ import okhttp3.CookieJar | ||||
| import okhttp3.HttpUrl | ||||
| 
 | ||||
| class CookieJarImpl( | ||||
|     private val sCookieStore: HashMap<String, List<Cookie>> | ||||
|     private val cookieStore: HashMap<String, List<Cookie>> | ||||
| ) : CookieJar { | ||||
| 
 | ||||
|     fun containsCookieWithName(cookies: List<Cookie>, name: String): Boolean { | ||||
| @ -52,12 +52,11 @@ class CookieJarImpl( | ||||
| 
 | ||||
|     override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) { | ||||
|         // Avoid duplicated cookies but update | ||||
|         val currentCookies: List<Cookie> = sCookieStore[url.host] ?: ArrayList() | ||||
|         val currentCookies: List<Cookie> = cookieStore[url.host] ?: ArrayList() | ||||
|         val updatedCookies: List<Cookie> = getUpdatedCookies(currentCookies, cookies) | ||||
|         sCookieStore[url.host] = updatedCookies | ||||
|         cookieStore[url.host] = updatedCookies | ||||
|     } | ||||
| 
 | ||||
|     override fun loadForRequest(url: HttpUrl) = | ||||
|         sCookieStore[url.host] ?: ArrayList() | ||||
| 
 | ||||
|         cookieStore[url.host] ?: ArrayList() | ||||
| } | ||||
| @ -21,26 +21,11 @@ | ||||
|  *   THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| package com.owncloud.android.lib.sampleclient; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.ArrayAdapter; | ||||
| import android.widget.TextView; | ||||
| package com.owncloud.android.lib.common.http | ||||
| 
 | ||||
| import com.owncloud.android.lib.resources.files.RemoteFile; | ||||
| import okhttp3.Interceptor | ||||
| 
 | ||||
| public class FilesArrayAdapter extends ArrayAdapter<RemoteFile> { | ||||
| 
 | ||||
|     public FilesArrayAdapter(Context context, int resource) { | ||||
|         super(context, resource); | ||||
|     } | ||||
| 
 | ||||
|     public View getView(int position, View convertView, ViewGroup parent) { | ||||
|         TextView textView = (TextView) super.getView(position, convertView, parent); | ||||
|         textView.setText(getItem(position).getRemotePath()); | ||||
|         return textView; | ||||
|     } | ||||
| class DummyInterceptor : Interceptor { | ||||
|     override fun intercept(chain: Interceptor.Chain) = chain.proceed(chain.request()) | ||||
| } | ||||
| 
 | ||||
| @ -40,7 +40,6 @@ import javax.net.ssl.SSLContext; | ||||
| import javax.net.ssl.SSLSocketFactory; | ||||
| import javax.net.ssl.TrustManager; | ||||
| import javax.net.ssl.X509TrustManager; | ||||
| import java.security.KeyManagementException; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| @ -54,32 +53,46 @@ import java.util.concurrent.TimeUnit; | ||||
|  */ | ||||
| 
 | ||||
| public class HttpClient { | ||||
|     private static OkHttpClient sOkHttpClient; | ||||
|     private static Context sContext; | ||||
|     private static HashMap<String, List<Cookie>> sCookieStore = new HashMap<>(); | ||||
|     private static LogInterceptor sLogInterceptor; | ||||
|     private Context mContext; | ||||
|     private HashMap<String, List<Cookie>> mCookieStore = new HashMap<>(); | ||||
|     private LogInterceptor mLogInterceptor = new LogInterceptor(); | ||||
| 
 | ||||
|     public static OkHttpClient getOkHttpClient() { | ||||
|         if (sOkHttpClient == null) { | ||||
|     private OkHttpClient mOkHttpClient = null; | ||||
| 
 | ||||
|     protected HttpClient(Context context) { | ||||
|         if (context == null) { | ||||
|             Timber.e("Context may not be NULL!"); | ||||
|             throw new NullPointerException("Context may not be NULL!"); | ||||
|         } | ||||
|         mContext = context; | ||||
|     } | ||||
| 
 | ||||
|     public OkHttpClient getOkHttpClient() { | ||||
|         if (mOkHttpClient == null) { | ||||
|             try { | ||||
|                 final X509TrustManager trustManager = new AdvancedX509TrustManager( | ||||
|                         NetworkUtils.getKnownServersStore(sContext)); | ||||
|                 final SSLSocketFactory sslSocketFactory = getNewSslSocketFactory(trustManager); | ||||
|                         NetworkUtils.getKnownServersStore(mContext)); | ||||
| 
 | ||||
|                 final SSLContext sslContext = buildSSLContext(); | ||||
|                 sslContext.init(null, new TrustManager[]{trustManager}, null); | ||||
|                 final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); | ||||
| 
 | ||||
|                 // Automatic cookie handling, NOT PERSISTENT | ||||
|                 final CookieJar cookieJar = new CookieJarImpl(sCookieStore); | ||||
| 
 | ||||
|                 // TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok. | ||||
|                 //.hostnameVerifier(new BrowserCompatHostnameVerifier()); | ||||
|                 sOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar); | ||||
|                 final CookieJar cookieJar = new CookieJarImpl(mCookieStore); | ||||
|                 mOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar); | ||||
| 
 | ||||
|             } catch (NoSuchAlgorithmException nsae) { | ||||
|                 Timber.e(nsae, "Could not setup SSL system."); | ||||
|                 throw new RuntimeException("Could not setup okHttp client.", nsae); | ||||
|             } catch (Exception e) { | ||||
|                 Timber.e(e, "Could not setup SSL system."); | ||||
|                 Timber.e(e, "Could not setup okHttp client."); | ||||
|                 throw new RuntimeException("Could not setup okHttp client.", e); | ||||
|             } | ||||
|         } | ||||
|         return sOkHttpClient; | ||||
|         return mOkHttpClient; | ||||
|     } | ||||
| 
 | ||||
|     private static SSLContext getSslContext() throws NoSuchAlgorithmException { | ||||
|     private SSLContext buildSSLContext() throws NoSuchAlgorithmException { | ||||
|         try { | ||||
|             return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName()); | ||||
|         } catch (NoSuchAlgorithmException tlsv13Exception) { | ||||
| @ -100,17 +113,11 @@ public class HttpClient { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static SSLSocketFactory getNewSslSocketFactory(X509TrustManager trustManager) | ||||
|             throws NoSuchAlgorithmException, KeyManagementException { | ||||
|         final SSLContext sslContext = getSslContext(); | ||||
|         sslContext.init(null, new TrustManager[]{trustManager}, null); | ||||
|         return sslContext.getSocketFactory(); | ||||
|     } | ||||
| 
 | ||||
|     private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager, | ||||
|     private OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager, | ||||
|                                               CookieJar cookieJar) { | ||||
|         return new OkHttpClient.Builder() | ||||
|                 .addNetworkInterceptor(getLogInterceptor()) | ||||
|                 .addNetworkInterceptor(DebugInterceptorFactory.INSTANCE.getInterceptor()) | ||||
|                 .protocols(Collections.singletonList(Protocol.HTTP_1_1)) | ||||
|                 .readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) | ||||
|                 .writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) | ||||
| @ -122,26 +129,19 @@ public class HttpClient { | ||||
|                 .build(); | ||||
|     } | ||||
| 
 | ||||
|     public static LogInterceptor getLogInterceptor() { | ||||
|         if (sLogInterceptor == null) { | ||||
|             sLogInterceptor = new LogInterceptor(); | ||||
|         } | ||||
|         return sLogInterceptor; | ||||
|     } | ||||
| 
 | ||||
|     public static List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) { | ||||
|         return sCookieStore.get(httpUrl.host()); | ||||
|     } | ||||
| 
 | ||||
|     public Context getContext() { | ||||
|         return sContext; | ||||
|         return mContext; | ||||
|     } | ||||
| 
 | ||||
|     public static void setContext(Context context) { | ||||
|         sContext = context; | ||||
|     public LogInterceptor getLogInterceptor() { | ||||
|         return mLogInterceptor; | ||||
|     } | ||||
| 
 | ||||
|     public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) { | ||||
|         return mCookieStore.get(httpUrl.host()); | ||||
|     } | ||||
| 
 | ||||
|     public void clearCookies() { | ||||
|         sCookieStore.clear(); | ||||
|         mCookieStore.clear(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,3 +1,27 @@ | ||||
| /* 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.common.http.methods | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.http.HttpClient | ||||
| @ -14,23 +38,41 @@ import java.net.URL | ||||
| import java.util.concurrent.TimeUnit | ||||
| 
 | ||||
| abstract class HttpBaseMethod constructor(url: URL) { | ||||
|     var okHttpClient: OkHttpClient | ||||
|     var httpUrl: HttpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException() | ||||
|     var request: Request | ||||
|     var followPermanentRedirects = false | ||||
|     abstract var response: Response | ||||
| 
 | ||||
|     var call: Call? = null | ||||
| 
 | ||||
|     var followRedirects: Boolean = true | ||||
|     var retryOnConnectionFailure: Boolean = true | ||||
|     var connectionTimeoutVal: Long? = null | ||||
|     var connectionTimeoutUnit: TimeUnit? = null | ||||
|     var readTimeoutVal: Long? = null | ||||
|         private set | ||||
|     var readTimeoutUnit: TimeUnit? = null | ||||
|         private set | ||||
| 
 | ||||
|     init { | ||||
|         okHttpClient = HttpClient.getOkHttpClient() | ||||
|         request = Request.Builder() | ||||
|             .url(httpUrl) | ||||
|             .build() | ||||
|     } | ||||
| 
 | ||||
|     @Throws(Exception::class) | ||||
|     open fun execute(): Int { | ||||
|         return onExecute() | ||||
|     open fun execute(httpClient: HttpClient): Int { | ||||
|         val okHttpClient = httpClient.okHttpClient.newBuilder().apply { | ||||
|             retryOnConnectionFailure(retryOnConnectionFailure) | ||||
|             followRedirects(followRedirects) | ||||
|             readTimeoutUnit?.let { unit -> | ||||
|                 readTimeoutVal?.let { readTimeout(it, unit) } | ||||
|             } | ||||
|             connectionTimeoutUnit?.let { unit -> | ||||
|                connectionTimeoutVal?.let { connectTimeout(it, unit) } | ||||
|             } | ||||
|         }.build() | ||||
| 
 | ||||
|         return onExecute(okHttpClient) | ||||
|     } | ||||
| 
 | ||||
|     open fun setUrl(url: HttpUrl) { | ||||
| @ -99,6 +141,11 @@ abstract class HttpBaseMethod constructor(url: URL) { | ||||
|         return response.body?.byteStream() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * returns the final url after following the last redirect. | ||||
|      */ | ||||
|     open fun getFinalUrl() = response.request.url | ||||
| 
 | ||||
|     /************************* | ||||
|      *** Connection Params *** | ||||
|      *************************/ | ||||
| @ -107,31 +154,19 @@ abstract class HttpBaseMethod constructor(url: URL) { | ||||
|     //         Setter | ||||
|     ////////////////////////////// | ||||
|     // Connection parameters | ||||
|     open fun setRetryOnConnectionFailure(retryOnConnectionFailure: Boolean) { | ||||
|         okHttpClient = okHttpClient.newBuilder() | ||||
|             .retryOnConnectionFailure(retryOnConnectionFailure) | ||||
|             .build() | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     open fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) { | ||||
|         okHttpClient = okHttpClient.newBuilder() | ||||
|             .readTimeout(readTimeout, timeUnit) | ||||
|             .build() | ||||
|         readTimeoutVal = readTimeout | ||||
|         readTimeoutUnit = timeUnit | ||||
|     } | ||||
| 
 | ||||
|     open fun setConnectionTimeout( | ||||
|         connectionTimeout: Long, | ||||
|         timeUnit: TimeUnit | ||||
|     ) { | ||||
|         okHttpClient = okHttpClient.newBuilder() | ||||
|             .readTimeout(connectionTimeout, timeUnit) | ||||
|             .build() | ||||
|     } | ||||
| 
 | ||||
|     open fun setFollowRedirects(followRedirects: Boolean) { | ||||
|         okHttpClient = okHttpClient.newBuilder() | ||||
|             .followRedirects(followRedirects) | ||||
|             .build() | ||||
|         connectionTimeoutVal = connectionTimeout | ||||
|         connectionTimeoutUnit = timeUnit | ||||
|     } | ||||
| 
 | ||||
|     /************ | ||||
| @ -148,5 +183,5 @@ abstract class HttpBaseMethod constructor(url: URL) { | ||||
|     //         For override | ||||
|     ////////////////////////////// | ||||
|     @Throws(Exception::class) | ||||
|     protected abstract fun onExecute(): Int | ||||
|     protected abstract fun onExecute(okHttpClient: OkHttpClient): Int | ||||
| } | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http.methods.nonwebdav | ||||
| 
 | ||||
| import okhttp3.OkHttpClient | ||||
| import java.io.IOException | ||||
| import java.net.URL | ||||
| 
 | ||||
| @ -33,10 +34,10 @@ import java.net.URL | ||||
|  */ | ||||
| class DeleteMethod(url: URL) : HttpMethod(url) { | ||||
|     @Throws(IOException::class) | ||||
|     override fun onExecute(): Int { | ||||
|     override fun onExecute(okHttpClient: OkHttpClient): Int { | ||||
|         request = request.newBuilder() | ||||
|             .delete() | ||||
|             .build() | ||||
|         return super.onExecute() | ||||
|         return super.onExecute(okHttpClient) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http.methods.nonwebdav | ||||
| 
 | ||||
| import okhttp3.OkHttpClient | ||||
| import java.io.IOException | ||||
| import java.net.URL | ||||
| 
 | ||||
| @ -33,10 +34,10 @@ import java.net.URL | ||||
|  */ | ||||
| class GetMethod(url: URL) : HttpMethod(url) { | ||||
|     @Throws(IOException::class) | ||||
|     override fun onExecute(): Int { | ||||
|     override fun onExecute(okHttpClient: OkHttpClient): Int { | ||||
|         request = request.newBuilder() | ||||
|             .get() | ||||
|             .build() | ||||
|         return super.onExecute() | ||||
|         return super.onExecute(okHttpClient) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -24,6 +24,7 @@ | ||||
| package com.owncloud.android.lib.common.http.methods.nonwebdav | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.http.methods.HttpBaseMethod | ||||
| import okhttp3.OkHttpClient | ||||
| import okhttp3.Response | ||||
| import java.net.URL | ||||
| 
 | ||||
| @ -38,7 +39,7 @@ abstract class HttpMethod( | ||||
| 
 | ||||
|     override lateinit var response: Response | ||||
| 
 | ||||
|     public override fun onExecute(): Int { | ||||
|     public override fun onExecute(okHttpClient: OkHttpClient): Int { | ||||
|         call = okHttpClient.newCall(request) | ||||
|         call?.let { response = it.execute() } | ||||
|         return super.statusCode | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http.methods.nonwebdav | ||||
| 
 | ||||
| import okhttp3.OkHttpClient | ||||
| import okhttp3.RequestBody | ||||
| import java.io.IOException | ||||
| import java.net.URL | ||||
| @ -37,10 +38,10 @@ class PostMethod( | ||||
|     private val postRequestBody: RequestBody | ||||
| ) : HttpMethod(url) { | ||||
|     @Throws(IOException::class) | ||||
|     override fun onExecute(): Int { | ||||
|     override fun onExecute(okHttpClient: OkHttpClient): Int { | ||||
|         request = request.newBuilder() | ||||
|             .post(postRequestBody) | ||||
|             .build() | ||||
|         return super.onExecute() | ||||
|         return super.onExecute(okHttpClient) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http.methods.nonwebdav | ||||
| 
 | ||||
| import okhttp3.OkHttpClient | ||||
| import okhttp3.RequestBody | ||||
| import java.io.IOException | ||||
| import java.net.URL | ||||
| @ -37,10 +38,10 @@ class PutMethod( | ||||
|     private val putRequestBody: RequestBody | ||||
| ) : HttpMethod(url) { | ||||
|     @Throws(IOException::class) | ||||
|     override fun onExecute(): Int { | ||||
|     override fun onExecute(okHttpClient: OkHttpClient): Int { | ||||
|         request = request.newBuilder() | ||||
|             .put(putRequestBody) | ||||
|             .build() | ||||
|         return super.onExecute() | ||||
|         return super.onExecute(okHttpClient) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http.methods.webdav | ||||
| 
 | ||||
| import at.bitfire.dav4jvm.DavOCResource | ||||
| import okhttp3.Response | ||||
| import java.net.URL | ||||
| 
 | ||||
| @ -38,7 +39,7 @@ class CopyMethod( | ||||
|     private val forceOverride: Boolean | ||||
| ) : DavMethod(url) { | ||||
|     @Throws(Exception::class) | ||||
|     public override fun onExecute(): Int { | ||||
|     public override fun onDavExecute(davResource: DavOCResource): Int { | ||||
|         davResource.copy( | ||||
|             destinationUrl, | ||||
|             forceOverride, | ||||
|  | ||||
| @ -29,14 +29,11 @@ import at.bitfire.dav4jvm.exception.HttpException | ||||
| import at.bitfire.dav4jvm.exception.RedirectException | ||||
| import com.owncloud.android.lib.common.http.HttpConstants | ||||
| import com.owncloud.android.lib.common.http.methods.HttpBaseMethod | ||||
| import okhttp3.HttpUrl | ||||
| import okhttp3.HttpUrl.Companion.toHttpUrlOrNull | ||||
| import okhttp3.OkHttpClient | ||||
| import okhttp3.Protocol | ||||
| import okhttp3.Response | ||||
| import okhttp3.ResponseBody.Companion.toResponseBody | ||||
| import java.net.MalformedURLException | ||||
| import java.net.URL | ||||
| import java.util.concurrent.TimeUnit | ||||
| 
 | ||||
| /** | ||||
|  * Wrapper to perform WebDAV (dav4android) calls | ||||
| @ -44,27 +41,25 @@ import java.util.concurrent.TimeUnit | ||||
|  * @author David González Verdugo | ||||
|  */ | ||||
| abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) { | ||||
|     protected var davResource: DavOCResource | ||||
| 
 | ||||
|     override lateinit var response: Response | ||||
|     private var davResource: DavOCResource? = null | ||||
| 
 | ||||
|     init { | ||||
|         val httpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException() | ||||
|     override fun abort() { | ||||
|         davResource?.cancelCall() | ||||
|     } | ||||
| 
 | ||||
|     protected abstract fun onDavExecute(davResource: DavOCResource): Int | ||||
| 
 | ||||
|     @Throws(Exception::class) | ||||
|     override fun onExecute(okHttpClient: OkHttpClient): Int { | ||||
|         return try { | ||||
|              davResource = DavOCResource( | ||||
|             okHttpClient, | ||||
|                 okHttpClient.newBuilder().followRedirects(false).build(), | ||||
|                 httpUrl, | ||||
|                 log | ||||
|             ) | ||||
|     } | ||||
| 
 | ||||
|     override fun abort() { | ||||
|         davResource.cancelCall() | ||||
|     } | ||||
| 
 | ||||
|     @Throws(Exception::class) | ||||
|     override fun execute(): Int { | ||||
|         return try { | ||||
|             onExecute() | ||||
|             onDavExecute(davResource!!) | ||||
|         } catch (httpException: HttpException) { | ||||
|             // Modify responses with information gathered from exceptions | ||||
|             if (httpException is RedirectException) { | ||||
| @ -91,71 +86,12 @@ abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ////////////////////////////// | ||||
|     //         Setter | ||||
|     ////////////////////////////// | ||||
|     // Connection parameters | ||||
|     override fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) { | ||||
|         super.setReadTimeout(readTimeout, timeUnit) | ||||
|         davResource = DavOCResource( | ||||
|             okHttpClient, | ||||
|             request.url, | ||||
|             log | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     override fun setConnectionTimeout( | ||||
|         connectionTimeout: Long, | ||||
|         timeUnit: TimeUnit | ||||
|     ) { | ||||
|         super.setConnectionTimeout(connectionTimeout, timeUnit) | ||||
|         davResource = DavOCResource( | ||||
|             okHttpClient, | ||||
|             request.url, | ||||
|             log | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     override fun setFollowRedirects(followRedirects: Boolean) { | ||||
|         super.setFollowRedirects(followRedirects) | ||||
|         davResource = DavOCResource( | ||||
|             okHttpClient, | ||||
|             request.url, | ||||
|             log | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     override fun setUrl(url: HttpUrl) { | ||||
|         super.setUrl(url) | ||||
|         davResource = DavOCResource( | ||||
|             okHttpClient, | ||||
|             request.url, | ||||
|             log | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     override fun setRequestHeader(name: String, value: String) { | ||||
|         super.setRequestHeader(name, value) | ||||
|         davResource = DavOCResource( | ||||
|             okHttpClient, | ||||
|             request.url, | ||||
|             log | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     ////////////////////////////// | ||||
|     //         Getter | ||||
|     ////////////////////////////// | ||||
|     override fun setRetryOnConnectionFailure(retryOnConnectionFailure: Boolean) { | ||||
|         super.setRetryOnConnectionFailure(retryOnConnectionFailure) | ||||
|         davResource = DavOCResource( | ||||
|             okHttpClient, | ||||
|             request.url, | ||||
|             log | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     override val isAborted: Boolean | ||||
|         get() = davResource.isCallAborted() | ||||
|         get() = davResource?.isCallAborted() ?: false | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http.methods.webdav | ||||
| 
 | ||||
| import at.bitfire.dav4jvm.DavOCResource | ||||
| import okhttp3.Response | ||||
| import java.net.URL | ||||
| 
 | ||||
| @ -34,7 +35,7 @@ import java.net.URL | ||||
|  */ | ||||
| class MkColMethod(url: URL) : DavMethod(url) { | ||||
|     @Throws(Exception::class) | ||||
|     public override fun onExecute(): Int { | ||||
|     public override fun onDavExecute(davResource: DavOCResource): Int { | ||||
|         davResource.mkCol( | ||||
|             xmlBody = null, | ||||
|             listOfHeaders = super.getRequestHeadersAsHashMap() | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http.methods.webdav | ||||
| 
 | ||||
| import at.bitfire.dav4jvm.DavOCResource | ||||
| import okhttp3.Response | ||||
| import java.net.URL | ||||
| 
 | ||||
| @ -38,7 +39,7 @@ class MoveMethod( | ||||
|     private val forceOverride: Boolean | ||||
| ) : DavMethod(url) { | ||||
|     @Throws(Exception::class) | ||||
|     public override fun onExecute(): Int { | ||||
|     override fun onDavExecute(davResource: DavOCResource): Int { | ||||
|         davResource.move( | ||||
|             destinationUrl, | ||||
|             forceOverride, | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http.methods.webdav | ||||
| 
 | ||||
| import at.bitfire.dav4jvm.DavOCResource | ||||
| import at.bitfire.dav4jvm.Property | ||||
| import at.bitfire.dav4jvm.Response | ||||
| import at.bitfire.dav4jvm.Response.HrefRelation | ||||
| @ -47,10 +48,10 @@ class PropfindMethod( | ||||
|         private set | ||||
| 
 | ||||
|     @Throws(IOException::class, DavException::class) | ||||
|     public override fun onExecute(): Int { | ||||
|     public override fun onDavExecute(davResource: DavOCResource): Int { | ||||
|         davResource.propfind( | ||||
|             depth = depth, | ||||
|             reqProp = *propertiesToRequest, | ||||
|             reqProp = propertiesToRequest, | ||||
|             listOfHeaders = super.getRequestHeadersAsHashMap(), | ||||
|             callback = { response: Response, hrefRelation: HrefRelation? -> | ||||
|                 when (hrefRelation) { | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http.methods.webdav | ||||
| 
 | ||||
| import at.bitfire.dav4jvm.DavOCResource | ||||
| import at.bitfire.dav4jvm.exception.HttpException | ||||
| import com.owncloud.android.lib.common.http.HttpConstants | ||||
| import okhttp3.RequestBody | ||||
| @ -39,7 +40,7 @@ class PutMethod( | ||||
|     private val putRequestBody: RequestBody | ||||
| ) : DavMethod(url) { | ||||
|     @Throws(IOException::class, HttpException::class) | ||||
|     public override fun onExecute(): Int { | ||||
|     public override fun onDavExecute(davResource: DavOCResource): Int { | ||||
|         davResource.put( | ||||
|             putRequestBody, | ||||
|             super.getRequestHeader(HttpConstants.IF_MATCH_HEADER), | ||||
|  | ||||
| @ -1,3 +1,27 @@ | ||||
| /* 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.common.operations; | ||||
| 
 | ||||
| import android.accounts.Account; | ||||
| @ -135,7 +159,7 @@ public abstract class RemoteOperation<T> implements Runnable { | ||||
|             if (mAccount != null && mContext != null) { | ||||
|                 OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); | ||||
|                 mClient = SingleSessionManager.getDefaultSingleton(). | ||||
|                         getClientFor(ocAccount, mContext); | ||||
|                         getClientFor(ocAccount, mContext, SingleSessionManager.getConnectionValidator()); | ||||
|             } else { | ||||
|                 throw new IllegalStateException("Trying to run a remote operation " + | ||||
|                         "asynchronously with no client and no chance to create one (no account)"); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2020 ownCloud GmbH. | ||||
|  *   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 | ||||
| @ -34,6 +34,7 @@ import com.owncloud.android.lib.common.http.HttpConstants; | ||||
| import com.owncloud.android.lib.common.http.methods.HttpBaseMethod; | ||||
| import com.owncloud.android.lib.common.network.CertificateCombinedException; | ||||
| import okhttp3.Headers; | ||||
| import org.apache.commons.lang3.exception.ExceptionUtils; | ||||
| import org.json.JSONException; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| @ -59,13 +60,15 @@ public class RemoteOperationResult<T> | ||||
|      * Generated - should be refreshed every time the class changes!! | ||||
|      */ | ||||
|     private static final long serialVersionUID = 4968939884332372230L; | ||||
|     private static final String LOCATION = "location"; | ||||
|     private static final String WWW_AUTHENTICATE = "www-authenticate"; | ||||
| 
 | ||||
|     private boolean mSuccess = false; | ||||
|     private int mHttpCode = -1; | ||||
|     private String mHttpPhrase = null; | ||||
|     private Exception mException = null; | ||||
|     private ResultCode mCode = ResultCode.UNKNOWN_ERROR; | ||||
|     private String mRedirectedLocation; | ||||
|     private String mRedirectedLocation = ""; | ||||
|     private List<String> mAuthenticate = new ArrayList<>(); | ||||
|     private String mLastPermanentLocation = null; | ||||
|     private T mData = null; | ||||
| @ -112,6 +115,14 @@ public class RemoteOperationResult<T> | ||||
|      */ | ||||
|     public RemoteOperationResult(Exception e) { | ||||
|         mException = e; | ||||
|         //TODO: Do propper exception handling and remove this | ||||
|         Timber.e("---------------------------------" + | ||||
|                         "\nCreate RemoteOperationResult from exception." + | ||||
|                         "\n Message: %s" + | ||||
|                         "\n Stacktrace: %s" + | ||||
|                         "\n---------------------------------", | ||||
|                 ExceptionUtils.getMessage(e), | ||||
|                 ExceptionUtils.getStackTrace(e)); | ||||
| 
 | ||||
|         if (e instanceof OperationCancelledException) { | ||||
|             mCode = ResultCode.CANCELLED; | ||||
| @ -248,11 +259,11 @@ public class RemoteOperationResult<T> | ||||
|         this(httpCode, httpPhrase); | ||||
|         if (headers != null) { | ||||
|             for (Map.Entry<String, List<String>> header : headers.toMultimap().entrySet()) { | ||||
|                 if ("location".equals(header.getKey().toLowerCase())) { | ||||
|                 if (LOCATION.equalsIgnoreCase(header.getKey())) { | ||||
|                     mRedirectedLocation = header.getValue().get(0); | ||||
|                     continue; | ||||
|                 } | ||||
|                 if ("www-authenticate".equals(header.getKey().toLowerCase())) { | ||||
|                 if (WWW_AUTHENTICATE.equalsIgnoreCase(header.getKey())) { | ||||
|                     for (String value: header.getValue()) { | ||||
|                         mAuthenticate.add(value.toLowerCase()); | ||||
|                     } | ||||
| @ -321,7 +332,7 @@ public class RemoteOperationResult<T> | ||||
|                     mHttpPhrase = errorMessage; | ||||
|                 } | ||||
|             } catch (Exception e) { | ||||
|                 Timber.w("Error reading exception from server: %s", e.getMessage()); | ||||
|                 Timber.w("Error reading exception from server: %s\nTrace: %s", e.getMessage(), ExceptionUtils.getStackTrace(e)); | ||||
|                 // mCode stays as set in this(success, httpCode, headers) | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @ -1,3 +1,27 @@ | ||||
| /* 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.common.utils | ||||
| 
 | ||||
| import info.hannes.timber.FileLoggingTree | ||||
|  | ||||
| @ -1,21 +1,27 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2022 ownCloud GmbH. | ||||
|  * | ||||
|  *   @author David González Verdugo | ||||
|  * | ||||
|  * Copyright (C) 2020 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: | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2, | ||||
|  * as published by the Free Software Foundation. | ||||
|  *   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. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * <p> | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources | ||||
|  | ||||
| @ -27,7 +27,6 @@ import com.owncloud.android.lib.common.OwnCloudClient | ||||
| import com.owncloud.android.lib.common.http.HttpConstants | ||||
| import com.owncloud.android.lib.common.http.methods.webdav.DavUtils.allPropset | ||||
| import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod | ||||
| import com.owncloud.android.lib.common.network.RedirectionPath | ||||
| import com.owncloud.android.lib.common.network.WebdavUtils | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperation | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| @ -44,26 +43,18 @@ import java.util.concurrent.TimeUnit | ||||
|  * @author Abel García de Prada | ||||
|  * | ||||
|  * @param remotePath      Path to append to the URL owned by the client instance. | ||||
|  * @param isUserLogged    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 | ||||
|  *                        needed to check user credentials | ||||
|  */ | ||||
| class CheckPathExistenceRemoteOperation( | ||||
|     val remotePath: String? = "", | ||||
|     val isUserLogged: Boolean | ||||
|     val isUserLoggedIn: Boolean | ||||
| ) : RemoteOperation<Boolean>() { | ||||
|     /** | ||||
|      * Gets the sequence of redirections followed during the execution of the operation. | ||||
|      * | ||||
|      * @return Sequence of redirections followed, if any, or NULL if the operation was not executed. | ||||
|      */ | ||||
|     var redirectionPath: RedirectionPath? = null | ||||
|         private set | ||||
| 
 | ||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> { | ||||
|         val previousFollowRedirects = client.followRedirects() | ||||
|         return try { | ||||
|             val stringUrl = | ||||
|                 if (isUserLogged) client.baseFilesWebDavUri.toString() | ||||
|                 if (isUserLoggedIn) client.baseFilesWebDavUri.toString() | ||||
|                 else client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath) | ||||
| 
 | ||||
|             val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropset).apply { | ||||
| @ -71,12 +62,7 @@ class CheckPathExistenceRemoteOperation( | ||||
|                 setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) | ||||
|             } | ||||
| 
 | ||||
|             client.setFollowRedirects(false) | ||||
|             var status = client.executeHttpMethod(propFindMethod) | ||||
|             if (previousFollowRedirects) { | ||||
|                 redirectionPath = client.followRedirection(propFindMethod) | ||||
|                 status = redirectionPath?.lastStatus!! | ||||
|             } | ||||
|             /* PROPFIND method | ||||
|              * 404 NOT FOUND: path doesn't exist, | ||||
|              * 207 MULTI_STATUS: path exists. | ||||
| @ -94,16 +80,9 @@ class CheckPathExistenceRemoteOperation( | ||||
|                 "Existence check for ${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)} : ${result.logMessage}" | ||||
|             ) | ||||
|             result | ||||
|         } finally { | ||||
|             client.setFollowRedirects(previousFollowRedirects) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return 'True' if the operation was executed and at least one redirection was followed. | ||||
|      */ | ||||
|     fun wasRedirected() = redirectionPath?.redirectionsCount ?: 0 > 0 | ||||
| 
 | ||||
|     private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS | ||||
| 
 | ||||
|     companion object { | ||||
|  | ||||
| @ -92,7 +92,8 @@ public class CopyRemoteFileOperation extends RemoteOperation<String> { | ||||
|         RemoteOperationResult result; | ||||
|         try { | ||||
|             CopyMethod copyMethod = | ||||
|                     new CopyMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mSrcRemotePath)), | ||||
|                     new CopyMethod( | ||||
|                             new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mSrcRemotePath)), | ||||
|                     client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath), | ||||
|                     mOverwrite); | ||||
| 
 | ||||
|  | ||||
| @ -87,7 +87,8 @@ public class CreateRemoteFolderOperation extends RemoteOperation { | ||||
|         RemoteOperationResult result; | ||||
|         try { | ||||
|             Uri webDavUri = createChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri(); | ||||
|             final MkColMethod mkcol = new MkColMethod(new URL(webDavUri + WebdavUtils.encodePath(mRemotePath))); | ||||
|             final MkColMethod mkcol = new MkColMethod( | ||||
|                     new URL(webDavUri + WebdavUtils.encodePath(mRemotePath))); | ||||
|             mkcol.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS); | ||||
|             mkcol.setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS); | ||||
|             final int status = client.executeHttpMethod(mkcol); | ||||
|  | ||||
| @ -0,0 +1,77 @@ | ||||
| /* 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.files | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.OwnCloudClient | ||||
| import com.owncloud.android.lib.common.http.HttpConstants | ||||
| import com.owncloud.android.lib.common.http.methods.webdav.DavUtils | ||||
| import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperation | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| import timber.log.Timber | ||||
| import java.net.URL | ||||
| import java.util.concurrent.TimeUnit | ||||
| 
 | ||||
| /** | ||||
|  * Operation to get the base url, which might differ in case of a redirect. | ||||
|  * | ||||
|  * @author Christian Schabesberger | ||||
|  */ | ||||
| 
 | ||||
| class GetBaseUrlRemoteOperation : RemoteOperation<String?>() { | ||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<String?> { | ||||
|         return try { | ||||
|             val stringUrl = client.baseFilesWebDavUri.toString() | ||||
| 
 | ||||
|             val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropset).apply { | ||||
|                 setReadTimeout(TIMEOUT, TimeUnit.SECONDS) | ||||
|                 setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS) | ||||
|             } | ||||
| 
 | ||||
|             val status = client.executeHttpMethod(propFindMethod) | ||||
| 
 | ||||
|             if (isSuccess(status)) { | ||||
|                 RemoteOperationResult<String?>(RemoteOperationResult.ResultCode.OK).apply { | ||||
|                     data = propFindMethod.getFinalUrl().toString() | ||||
|                 } | ||||
|             } else { | ||||
|                 RemoteOperationResult<String?>(propFindMethod).apply { | ||||
|                     data = null | ||||
|                 } | ||||
|             } | ||||
|         } catch (e: Exception) { | ||||
|             Timber.e(e, "Could not get actuall (or redirected) base URL from base url (/).") | ||||
|             RemoteOperationResult<String?>(e) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS | ||||
| 
 | ||||
|     companion object { | ||||
|         /** | ||||
|          * Maximum time to wait for a response from the server in milliseconds. | ||||
|          */ | ||||
|         private const val TIMEOUT = 10_000L | ||||
|     } | ||||
| } | ||||
| @ -76,7 +76,8 @@ public class ReadRemoteFileOperation extends RemoteOperation<RemoteFile> { | ||||
|         /// take the duty of check the server for the current state of the file there | ||||
|         try { | ||||
|             // remote request | ||||
|             propfind = new PropfindMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), | ||||
|             propfind = new PropfindMethod( | ||||
|                     new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), | ||||
|                     DEPTH_0, | ||||
|                     DavUtils.getAllPropset()); | ||||
| 
 | ||||
|  | ||||
| @ -77,8 +77,6 @@ public class ReadRemoteFolderOperation extends RemoteOperation<ArrayList<RemoteF | ||||
|                     DavConstants.DEPTH_1, | ||||
|                     DavUtils.getAllPropset()); | ||||
| 
 | ||||
|             client.setFollowRedirects(true); | ||||
| 
 | ||||
|             int status = client.executeHttpMethod(propfindMethod); | ||||
| 
 | ||||
|             if (isSuccess(status)) { | ||||
|  | ||||
| @ -91,7 +91,8 @@ public class RenameRemoteFileOperation extends RemoteOperation { | ||||
|                 return new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE); | ||||
|             } | ||||
| 
 | ||||
|             final MoveMethod move = new MoveMethod(new URL(client.getUserFilesWebDavUri() + | ||||
|             final MoveMethod move = new MoveMethod( | ||||
|                     new URL(client.getUserFilesWebDavUri() + | ||||
|                     WebdavUtils.encodePath(mOldRemotePath)), | ||||
|                     client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mNewRemotePath), false); | ||||
| 
 | ||||
|  | ||||
| @ -50,7 +50,7 @@ class UploadFileFromContentUriOperation( | ||||
| 
 | ||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> { | ||||
|         val putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(uploadPath)), requestBody).apply { | ||||
|             setRetryOnConnectionFailure(false) | ||||
|             retryOnConnectionFailure = false | ||||
|             addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, requestBody.contentLength().toString()) | ||||
|             addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, lastModified) | ||||
|         } | ||||
|  | ||||
| @ -92,7 +92,8 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation | ||||
|                 result = new RemoteOperationResult<>(new OperationCancelledException()); | ||||
|                 break; | ||||
|             } else { | ||||
|                 mPutMethod = new PutMethod(new URL(uriPrefix + File.separator + chunkIndex), mFileRequestBody); | ||||
|                 mPutMethod = new PutMethod( | ||||
|                         new URL(uriPrefix + File.separator + chunkIndex), mFileRequestBody); | ||||
| 
 | ||||
|                 if (chunkIndex == chunkCount - 1) { | ||||
|                     // Added a high timeout to the last chunk due to when the last chunk | ||||
|  | ||||
| @ -1,22 +1,26 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2022 ownCloud GmbH. | ||||
|  * | ||||
|  * @author Abel García de Prada | ||||
|  * Copyright (C) 2020 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: | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2, | ||||
|  * as published by the Free Software Foundation. | ||||
|  *   The above copyright notice and this permission notice shall be included in | ||||
|  *   all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *   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. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.files.services.implementation | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.OwnCloudClient | ||||
| @ -29,6 +33,6 @@ class OCFileService(override val client: OwnCloudClient) : | ||||
|     override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean> = | ||||
|         CheckPathExistenceRemoteOperation( | ||||
|             remotePath = path, | ||||
|             isUserLogged = isUserLogged | ||||
|             isUserLoggedIn = isUserLogged | ||||
|         ).execute(client) | ||||
| } | ||||
|  | ||||
| @ -55,6 +55,7 @@ class GetOIDCDiscoveryRemoteOperation : RemoteOperation<OIDCDiscoveryResponse>() | ||||
|                 addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) | ||||
|             } | ||||
| 
 | ||||
|             getMethod.followRedirects = true | ||||
|             val status = client.executeHttpMethod(getMethod) | ||||
| 
 | ||||
|             val responseBody = getMethod.getResponseBodyAsString() | ||||
|  | ||||
| @ -138,7 +138,6 @@ class CreateRemoteShareOperation( | ||||
|     } | ||||
| 
 | ||||
|     private fun createFormBody(): FormBody { | ||||
| 
 | ||||
|         val formBodyBuilder = FormBody.Builder() | ||||
|             .add(PARAM_PATH, remoteFilePath) | ||||
|             .add(PARAM_SHARE_TYPE, shareType.value.toString()) | ||||
|  | ||||
| @ -104,6 +104,7 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper | ||||
| 
 | ||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> { | ||||
| 
 | ||||
| 
 | ||||
|         val requestUri = buildRequestUri(client.baseUri) | ||||
| 
 | ||||
|         val deleteMethod = DeleteMethod(URL(requestUri.toString())).apply { | ||||
|  | ||||
| @ -195,6 +195,7 @@ class UpdateRemoteShareOperation | ||||
|         return formBodyBuilder | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> { | ||||
|         val requestUri = buildRequestUri(client.baseUri) | ||||
| 
 | ||||
| @ -205,7 +206,6 @@ class UpdateRemoteShareOperation | ||||
|             addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         return try { | ||||
|             val status = client.executeHttpMethod(putMethod) | ||||
|             val response = putMethod.getResponseBodyAsString() | ||||
|  | ||||
| @ -1,21 +1,27 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2022 ownCloud GmbH. | ||||
|  * | ||||
|  *   @author David González Verdugo | ||||
|  * | ||||
|  * Copyright (C) 2020 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: | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2, | ||||
|  * as published by the Free Software Foundation. | ||||
|  *   The above copyright notice and this permission notice shall be included in | ||||
|  *   all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *   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. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.shares.services | ||||
|  | ||||
| @ -1,22 +1,28 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2022 ownCloud GmbH. | ||||
|  * | ||||
|  *   @author Christian Schabesberger | ||||
|  *   @author David González Verdugo | ||||
|  * | ||||
|  * Copyright (C) 2020 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: | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2, | ||||
|  * as published by the Free Software Foundation. | ||||
|  *   The above copyright notice and this permission notice shall be included in | ||||
|  *   all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *   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. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.shares.services | ||||
|  | ||||
| @ -1,21 +1,27 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2022 ownCloud GmbH. | ||||
|  * | ||||
|  *   @author David González Verdugo | ||||
|  * | ||||
|  * Copyright (C) 2020 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: | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2, | ||||
|  * as published by the Free Software Foundation. | ||||
|  *   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. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * <p> | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.shares.services.implementation | ||||
|  | ||||
| @ -1,21 +1,26 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
|  /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2022 ownCloud GmbH. | ||||
|  * | ||||
|  *  @author David González Verdugo | ||||
|  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  *   of this software and associated documentation files (the "Software"), to deal | ||||
|  *   in the Software without restriction, including without limitation the rights | ||||
|  *   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: | ||||
|  * | ||||
|  * Copyright (C) 2020 ownCloud GmbH. | ||||
|  *   The above copyright notice and this permission notice shall be included in | ||||
|  *   all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2, | ||||
|  * as published by the Free Software Foundation. | ||||
|  *   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. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * <p> | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.shares.services.implementation | ||||
|  | ||||
| @ -30,9 +30,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode | ||||
| import com.owncloud.android.lib.resources.status.HttpScheme.HTTPS_PREFIX | ||||
| import com.owncloud.android.lib.resources.status.HttpScheme.HTTP_PREFIX | ||||
| import com.owncloud.android.lib.resources.status.HttpScheme.HTTP_SCHEME | ||||
| import org.json.JSONException | ||||
| import timber.log.Timber | ||||
| 
 | ||||
| /** | ||||
|  * Checks if the server is valid | ||||
| @ -45,27 +43,25 @@ import timber.log.Timber | ||||
| class GetRemoteStatusOperation : RemoteOperation<RemoteServerInfo>() { | ||||
| 
 | ||||
|     public override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> { | ||||
|         if (!usesHttpOrHttps(client.baseUri)) { | ||||
|             client.baseUri = buildFullHttpsUrl(client.baseUri) | ||||
| 
 | ||||
|         var result = tryToConnect(client) | ||||
|         if (!(result.code == ResultCode.OK || result.code == ResultCode.OK_SSL) && !result.isSslRecoverableException) { | ||||
|             Timber.d("Establishing secure connection failed, trying non secure connection") | ||||
|             client.baseUri = client.baseUri.buildUpon().scheme(HTTP_SCHEME).build() | ||||
|             result = tryToConnect(client) | ||||
|         } | ||||
|         return tryToConnect(client) | ||||
|     } | ||||
| 
 | ||||
|         return result | ||||
|     private fun updateClientBaseUrl(client: OwnCloudClient, newBaseUrl: String) { | ||||
|         client.baseUri = Uri.parse(newBaseUrl) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private fun tryToConnect(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> { | ||||
|         val baseUrl = client.baseUri.toString() | ||||
|         client.setFollowRedirects(false) | ||||
|         return try { | ||||
|             val requester = StatusRequester() | ||||
|             val requestResult = requester.requestAndFollowRedirects(baseUrl, client) | ||||
|             requester.handleRequestResult(requestResult, baseUrl).also { | ||||
|                 client.baseUri = Uri.parse(it.data.baseUrl) | ||||
|             } | ||||
|             val requestResult = requester.request(baseUrl, client) | ||||
|             val result = requester.handleRequestResult(requestResult, baseUrl) | ||||
|             updateClientBaseUrl(client, result.data.baseUrl) | ||||
|             return result | ||||
|         } catch (e: JSONException) { | ||||
|             RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED) | ||||
|         } catch (e: Exception) { | ||||
|  | ||||
| @ -73,36 +73,18 @@ internal class StatusRequester { | ||||
|     data class RequestResult( | ||||
|         val getMethod: GetMethod, | ||||
|         val status: Int, | ||||
|         val redirectedToUnsecureLocation: Boolean, | ||||
|         val lastLocation: String | ||||
|     ) | ||||
| 
 | ||||
|     fun requestAndFollowRedirects(baseLocation: String, client: OwnCloudClient): RequestResult { | ||||
|         var currentLocation = baseLocation + OwnCloudClient.STATUS_PATH | ||||
|         var redirectedToUnsecureLocation = false | ||||
|     fun request(baseLocation: String, client: OwnCloudClient): RequestResult { | ||||
|         val currentLocation = baseLocation + OwnCloudClient.STATUS_PATH | ||||
|         var status: Int | ||||
| 
 | ||||
|         while (true) { | ||||
|         val getMethod = getGetMethod(currentLocation) | ||||
| 
 | ||||
|         getMethod.followPermanentRedirects = true | ||||
|         status = client.executeHttpMethod(getMethod) | ||||
|             val result = | ||||
|                 if (status.isSuccess()) RemoteOperationResult<OwnCloudVersion>(RemoteOperationResult.ResultCode.OK) | ||||
|                 else RemoteOperationResult(getMethod) | ||||
| 
 | ||||
|             if (result.redirectedLocation.isNullOrEmpty() || result.isSuccess) { | ||||
|                 return RequestResult(getMethod, status, redirectedToUnsecureLocation, currentLocation) | ||||
|             } else { | ||||
|                 val nextLocation = updateLocationWithRedirectPath(currentLocation, result.redirectedLocation) | ||||
|                 redirectedToUnsecureLocation = | ||||
|                     isRedirectedToNonSecureConnection( | ||||
|                         redirectedToUnsecureLocation, | ||||
|                         currentLocation, | ||||
|                         nextLocation | ||||
|                     ) | ||||
|                 currentLocation = nextLocation | ||||
|             } | ||||
|         } | ||||
|         return RequestResult(getMethod, status, getMethod.getFinalUrl().toString()) | ||||
|     } | ||||
| 
 | ||||
|     private fun Int.isSuccess() = this == HttpConstants.HTTP_OK | ||||
| @ -122,12 +104,8 @@ internal class StatusRequester { | ||||
|         // the version object will be returned even if the version is invalid, no error code; | ||||
|         // every app will decide how to act if (ocVersion.isVersionValid() == false) | ||||
|         val result: RemoteOperationResult<RemoteServerInfo> = | ||||
|             if (requestResult.redirectedToUnsecureLocation) { | ||||
|                 RemoteOperationResult(RemoteOperationResult.ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION) | ||||
|             } else { | ||||
|             if (baseUrl.startsWith(HTTPS_SCHEME)) RemoteOperationResult(RemoteOperationResult.ResultCode.OK_SSL) | ||||
|             else RemoteOperationResult(RemoteOperationResult.ResultCode.OK_NO_SSL) | ||||
|             } | ||||
|         val finalUrl = URL(requestResult.lastLocation) | ||||
|         val finalBaseUrl = URL( | ||||
|             finalUrl.protocol, | ||||
|  | ||||
| @ -1,21 +1,27 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2022 ownCloud GmbH. | ||||
|  * | ||||
|  *   @author David González Verdugo | ||||
|  * | ||||
|  * Copyright (C) 2020 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: | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2, | ||||
|  * as published by the Free Software Foundation. | ||||
|  *   The above copyright notice and this permission notice shall be included in | ||||
|  *   all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *   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. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.status.services | ||||
|  | ||||
| @ -23,12 +23,13 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.resources.status.services | ||||
| 
 | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.OwnCloudClient | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| import com.owncloud.android.lib.resources.status.RemoteServerInfo | ||||
| 
 | ||||
| interface ServerInfoService { | ||||
|     fun checkPathExistence(path: String, isUserLogged: Boolean, client: OwnCloudClient): RemoteOperationResult<Boolean> | ||||
|     fun checkPathExistence(path: String, isUserLoggedIn: Boolean, client: OwnCloudClient): RemoteOperationResult<Boolean> | ||||
| 
 | ||||
|     fun getRemoteStatus(path: String, client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> | ||||
| } | ||||
|  | ||||
| @ -1,21 +1,27 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2022 ownCloud GmbH. | ||||
|  * | ||||
|  *   @author David González Verdugo | ||||
|  *  | ||||
|  * Copyright (C) 2020 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: | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2, | ||||
|  * as published by the Free Software Foundation. | ||||
|  *   The above copyright notice and this permission notice shall be included in | ||||
|  *   all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *   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. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.status.services.implementation | ||||
|  | ||||
| @ -1,24 +1,32 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2022 ownCloud GmbH. | ||||
|  * | ||||
|  * @author Abel García de Prada  | ||||
|  * Copyright (C) 2020 ownCloud GmbH. | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2, | ||||
|  * as published by the Free Software Foundation. | ||||
|  *   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: | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *   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. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.status.services.implementation | ||||
| 
 | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.OwnCloudClient | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation | ||||
| @ -30,12 +38,12 @@ class OCServerInfoService : ServerInfoService { | ||||
| 
 | ||||
|     override fun checkPathExistence( | ||||
|         path: String, | ||||
|         isUserLogged: Boolean, | ||||
|         isUserLoggedIn: Boolean, | ||||
|         client: OwnCloudClient | ||||
|     ): RemoteOperationResult<Boolean> = | ||||
|         CheckPathExistenceRemoteOperation( | ||||
|             remotePath = path, | ||||
|             isUserLogged = true | ||||
|             isUserLoggedIn = true | ||||
|         ).execute(client) | ||||
| 
 | ||||
|     override fun getRemoteStatus( | ||||
|  | ||||
| @ -1,20 +1,27 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2022 ownCloud GmbH. | ||||
|  * | ||||
|  *   @author Abel García de Prada | ||||
|  * Copyright (C) 2020 ownCloud GmbH. | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2, | ||||
|  * as published by the Free Software Foundation. | ||||
|  *   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. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * <p> | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.users.services.implementation | ||||
|  | ||||
| @ -0,0 +1,29 @@ | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2020 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.common.http | ||||
| 
 | ||||
| object DebugInterceptorFactory { | ||||
|     fun getInterceptor() = DummyInterceptor() | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| apply plugin: 'com.android.application' | ||||
| 
 | ||||
| dependencies { | ||||
|     implementation project(':owncloudComLibrary') | ||||
| } | ||||
| 
 | ||||
| android { | ||||
|     compileSdkVersion 30 | ||||
| 
 | ||||
|     defaultConfig { | ||||
|         minSdkVersion 21 | ||||
|         targetSdkVersion 30 | ||||
| 
 | ||||
|         // This is pretty ugly but manifest placeholders don't seem to work very well when using different modules | ||||
|         // See https://github.com/openid/AppAuth-Android/issues/325 | ||||
|         manifestPlaceholders = [appAuthRedirectScheme: ''] | ||||
|     } | ||||
| 
 | ||||
|     lintOptions { | ||||
|         abortOnError false | ||||
|     } | ||||
| 
 | ||||
|     packagingOptions { | ||||
|         exclude 'META-INF/LICENSE.txt' | ||||
|     } | ||||
| 
 | ||||
|     compileOptions { | ||||
|         sourceCompatibility JavaVersion.VERSION_1_8 | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 1.8 MiB | 
| @ -1 +0,0 @@ | ||||
| Testing ownCloud uploads | ||||
| @ -1,47 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!--  ownCloud Android Library is available under MIT license | ||||
|    Copyright (C) 2016 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. | ||||
| 
 | ||||
|  --> | ||||
| 
 | ||||
| <manifest package="com.owncloud.android.lib.sampleclient" | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:versionCode="1" | ||||
|     android:versionName="1.0"> | ||||
| 
 | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
|     <application | ||||
|         android:icon="@drawable/ic_launcher" | ||||
|         android:label="@string/app_name"> | ||||
|         <activity | ||||
|             android:name="MainActivity" | ||||
|             android:configChanges="orientation|keyboardHidden" | ||||
|             android:label="@string/app_name" | ||||
|             android:screenOrientation="portrait" | ||||
|             > | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN" /> | ||||
|                 <category android:name="android.intent.category.LAUNCHER" /> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|     </application> | ||||
| </manifest> | ||||
| @ -1,300 +0,0 @@ | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2020 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.sampleclient; | ||||
| 
 | ||||
| import android.annotation.SuppressLint; | ||||
| import android.app.Activity; | ||||
| import android.content.pm.PackageInfo; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.content.res.AssetManager; | ||||
| import android.graphics.drawable.BitmapDrawable; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.os.Handler; | ||||
| import android.view.View; | ||||
| import android.widget.ListView; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.OwnCloudClient; | ||||
| import com.owncloud.android.lib.common.OwnCloudClientFactory; | ||||
| import com.owncloud.android.lib.common.SingleSessionManager; | ||||
| import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; | ||||
| import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; | ||||
| import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperation; | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult; | ||||
| import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation; | ||||
| import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation; | ||||
| import com.owncloud.android.lib.resources.files.RemoteFile; | ||||
| import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation; | ||||
| import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation; | ||||
| import info.hannes.timber.DebugTree; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| public class MainActivity extends Activity implements OnRemoteOperationListener, OnDatatransferProgressListener { | ||||
| 
 | ||||
|     private Handler mHandler; | ||||
|     private OwnCloudClient mClient; | ||||
|     private FilesArrayAdapter mFilesAdapter; | ||||
|     private View mFrame; | ||||
| 
 | ||||
|     /** | ||||
|      * Called when the activity is first created. | ||||
|      */ | ||||
|     @Override | ||||
|     public void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.main); | ||||
| 
 | ||||
|         Timber.plant(new DebugTree()); | ||||
|         mHandler = new Handler(); | ||||
| 
 | ||||
|         final Uri serverUri = Uri.parse(getString(R.string.server_base_url)); | ||||
| 
 | ||||
|         SingleSessionManager.setUserAgent(getUserAgent()); | ||||
|         mClient = OwnCloudClientFactory.createOwnCloudClient(serverUri, this, true); | ||||
| 
 | ||||
|         mClient.setCredentials( | ||||
|                 OwnCloudCredentialsFactory.newBasicCredentials( | ||||
|                         getString(R.string.username), | ||||
|                         getString(R.string.password) | ||||
|                 ) | ||||
|         ); | ||||
| 
 | ||||
|         mFilesAdapter = new FilesArrayAdapter(this, R.layout.file_in_list); | ||||
|         ((ListView) findViewById(R.id.list_view)).setAdapter(mFilesAdapter); | ||||
| 
 | ||||
|         // TODO move to background thread or task | ||||
|         AssetManager assets = getAssets(); | ||||
|         try { | ||||
|             String sampleFileName = getString(R.string.sample_file_name); | ||||
|             File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path)); | ||||
|             upFolder.mkdir(); | ||||
|             File upFile = new File(upFolder, sampleFileName); | ||||
|             FileOutputStream fos = new FileOutputStream(upFile); | ||||
|             InputStream is = assets.open(sampleFileName); | ||||
|             int count; | ||||
|             byte[] buffer = new byte[1024]; | ||||
|             while ((count = is.read(buffer, 0, buffer.length)) >= 0) { | ||||
|                 fos.write(buffer, 0, count); | ||||
|             } | ||||
|             is.close(); | ||||
|             fos.close(); | ||||
|         } catch (IOException e) { | ||||
|             Toast.makeText(this, R.string.error_copying_sample_file, Toast.LENGTH_SHORT).show(); | ||||
|             Timber.e(e, getString(R.string.error_copying_sample_file)); | ||||
|         } | ||||
| 
 | ||||
|         mFrame = findViewById(R.id.frame); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path)); | ||||
|         File upFile = upFolder.listFiles()[0]; | ||||
|         upFile.delete(); | ||||
|         upFolder.delete(); | ||||
|         super.onDestroy(); | ||||
|     } | ||||
| 
 | ||||
|     public void onClickHandler(View button) { | ||||
|         switch (button.getId()) { | ||||
|             case R.id.button_refresh: | ||||
|                 startRefresh(); | ||||
|                 break; | ||||
|             case R.id.button_upload: | ||||
|                 startUpload(); | ||||
|                 break; | ||||
|             case R.id.button_delete_remote: | ||||
|                 startRemoteDeletion(); | ||||
|                 break; | ||||
|             case R.id.button_download: | ||||
|                 startDownload(); | ||||
|                 break; | ||||
|             case R.id.button_delete_local: | ||||
|                 startLocalDeletion(); | ||||
|                 break; | ||||
|             default: | ||||
|                 Toast.makeText(this, R.string.youre_doing_it_wrong, Toast.LENGTH_SHORT).show(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void startRefresh() { | ||||
|         ReadRemoteFolderOperation refreshOperation = new ReadRemoteFolderOperation(File.separator); | ||||
|         refreshOperation.execute(mClient, this, mHandler); | ||||
|     } | ||||
| 
 | ||||
|     private void startUpload() { | ||||
|         File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path)); | ||||
|         File fileToUpload = upFolder.listFiles()[0]; | ||||
|         String remotePath = File.separator + fileToUpload.getName(); | ||||
|         String mimeType = getString(R.string.sample_file_mimetype); | ||||
| 
 | ||||
|         // Get the last modification date of the file from the file system | ||||
|         long timeStampLong = fileToUpload.lastModified() / 1000; | ||||
|         String timeStamp = Long.toString(timeStampLong); | ||||
| 
 | ||||
|         UploadRemoteFileOperation uploadOperation = new UploadRemoteFileOperation(fileToUpload.getAbsolutePath(), | ||||
|                 remotePath, mimeType, timeStamp); | ||||
|         uploadOperation.addDatatransferProgressListener(this); | ||||
|         uploadOperation.execute(mClient, this, mHandler); | ||||
|     } | ||||
| 
 | ||||
|     private void startRemoteDeletion() { | ||||
|         File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path)); | ||||
|         File fileToUpload = upFolder.listFiles()[0]; | ||||
|         String remotePath = File.separator + fileToUpload.getName(); | ||||
| 
 | ||||
|         RemoveRemoteFileOperation removeOperation = new RemoveRemoteFileOperation(remotePath); | ||||
|         removeOperation.execute(mClient, this, mHandler); | ||||
|     } | ||||
| 
 | ||||
|     private void startDownload() { | ||||
|         File downFolder = new File(getCacheDir(), getString(R.string.download_folder_path)); | ||||
|         downFolder.mkdir(); | ||||
|         File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path)); | ||||
|         File fileToUpload = upFolder.listFiles()[0]; | ||||
|         String remotePath = File.separator + fileToUpload.getName(); | ||||
| 
 | ||||
|         DownloadRemoteFileOperation downloadOperation = new DownloadRemoteFileOperation(remotePath, | ||||
|                 downFolder.getAbsolutePath()); | ||||
|         downloadOperation.addDatatransferProgressListener(this); | ||||
|         downloadOperation.execute(mClient, this, mHandler); | ||||
|     } | ||||
| 
 | ||||
|     private void startLocalDeletion() { | ||||
|         File downFolder = new File(getCacheDir(), getString(R.string.download_folder_path)); | ||||
|         File downloadedFile = downFolder.listFiles()[0]; | ||||
|         if (!downloadedFile.delete() && downloadedFile.exists()) { | ||||
|             Toast.makeText(this, R.string.error_deleting_local_file, Toast.LENGTH_SHORT).show(); | ||||
|         } else { | ||||
|             ((TextView) findViewById(R.id.download_progress)).setText("0%"); | ||||
|             findViewById(R.id.frame).setBackgroundDrawable(null); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { | ||||
|         if (!result.isSuccess()) { | ||||
|             Toast.makeText(this, R.string.todo_operation_finished_in_fail, Toast.LENGTH_SHORT).show(); | ||||
|             Timber.e(result.getException(), result.getLogMessage()); | ||||
| 
 | ||||
|         } else if (operation instanceof ReadRemoteFolderOperation) { | ||||
|             onSuccessfulRefresh(result); | ||||
| 
 | ||||
|         } else if (operation instanceof com.owncloud.android.lib.resources.files.UploadRemoteFileOperation) { | ||||
|             onSuccessfulUpload(); | ||||
| 
 | ||||
|         } else if (operation instanceof RemoveRemoteFileOperation) { | ||||
|             onSuccessfulRemoteDeletion(); | ||||
| 
 | ||||
|         } else if (operation instanceof DownloadRemoteFileOperation) { | ||||
|             onSuccessfulDownload(); | ||||
| 
 | ||||
|         } else { | ||||
|             Toast.makeText(this, R.string.todo_operation_finished_in_success, Toast.LENGTH_SHORT).show(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void onSuccessfulRefresh(RemoteOperationResult result) { | ||||
|         mFilesAdapter.clear(); | ||||
|         List<RemoteFile> files = new ArrayList<>(); | ||||
|         for (RemoteFile remoteFile : (List<RemoteFile>) result.getData()) { | ||||
|             files.add(remoteFile); | ||||
|         } | ||||
|         for (RemoteFile file : files) { | ||||
|             mFilesAdapter.add(file); | ||||
|         } | ||||
|         mFilesAdapter.remove(mFilesAdapter.getItem(0)); | ||||
|         mFilesAdapter.notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
|     private void onSuccessfulUpload() { | ||||
|         startRefresh(); | ||||
|     } | ||||
| 
 | ||||
|     private void onSuccessfulRemoteDeletion() { | ||||
|         startRefresh(); | ||||
|         TextView progressView = findViewById(R.id.upload_progress); | ||||
|         if (progressView != null) { | ||||
|             progressView.setText("0%"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void onSuccessfulDownload() { | ||||
|         File downFolder = new File(getCacheDir(), getString(R.string.download_folder_path)); | ||||
|         File downloadedFile = downFolder.listFiles()[0]; | ||||
|         BitmapDrawable bDraw = new BitmapDrawable(getResources(), downloadedFile.getAbsolutePath()); | ||||
|         mFrame.setBackgroundDrawable(bDraw); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressLint("SetTextI18n") | ||||
|     @Override | ||||
|     public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) { | ||||
|         final long percentage = (totalToTransfer > 0 ? totalTransferredSoFar * 100 / totalToTransfer : 0); | ||||
|         final boolean upload = fileName.contains(getString(R.string.upload_folder_path)); | ||||
|         Timber.d("progressRate %s", percentage); | ||||
|         mHandler.post(() -> { | ||||
|             TextView progressView; | ||||
|             if (upload) { | ||||
|                 progressView = findViewById(R.id.upload_progress); | ||||
|             } else { | ||||
|                 progressView = findViewById(R.id.download_progress); | ||||
|             } | ||||
|             if (progressView != null) { | ||||
|                 progressView.setText(percentage + "%"); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     // user agent | ||||
|     @SuppressLint("StringFormatInvalid") | ||||
|     private String getUserAgent() { | ||||
|         String appString = getResources().getString(R.string.user_agent); | ||||
|         String packageName = getPackageName(); | ||||
|         String version = ""; | ||||
| 
 | ||||
|         PackageInfo pInfo; | ||||
|         try { | ||||
|             pInfo = getPackageManager().getPackageInfo(packageName, 0); | ||||
|             if (pInfo != null) { | ||||
|                 version = pInfo.versionName; | ||||
|             } | ||||
|         } catch (PackageManager.NameNotFoundException e) { | ||||
|             Timber.e(e); | ||||
|         } | ||||
| 
 | ||||
|         // Mozilla/5.0 (Android) ownCloud-android/1.7.0 | ||||
|         return String.format(appString, version); | ||||
|     } | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 7.3 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 3.6 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 4.8 KiB | 
| @ -1,29 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!--  ownCloud Android Library is available under MIT license | ||||
|    Copyright (C) 2016 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. | ||||
| 
 | ||||
|  --> | ||||
| 
 | ||||
| <TextView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     /> | ||||
| @ -1,122 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!--  ownCloud Android Library is available under MIT license | ||||
|    Copyright (C) 2016 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. | ||||
| 
 | ||||
|  --> | ||||
| 
 | ||||
| <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     > | ||||
| 
 | ||||
|     <Button | ||||
|         android:id="@+id/button_refresh" | ||||
|         style="@style/ButtonStyle" | ||||
|         android:layout_alignParentLeft="true" | ||||
|         android:layout_alignParentTop="true" | ||||
|         android:onClick="onClickHandler" | ||||
|         android:text="@string/refresh" | ||||
|         /> | ||||
| 
 | ||||
|     <ListView | ||||
|         android:id="@+id/list_view" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_above="@+id/button_upload" | ||||
|         android:layout_alignParentLeft="true" | ||||
|         android:layout_alignParentRight="true" | ||||
|         android:layout_below="@+id/button_refresh" | ||||
|         > | ||||
|     </ListView> | ||||
| 
 | ||||
|     <Button | ||||
|         android:id="@+id/button_upload" | ||||
|         style="@style/ButtonStyle" | ||||
|         android:layout_above="@+id/frame" | ||||
|         android:layout_alignParentLeft="true" | ||||
|         android:onClick="onClickHandler" | ||||
|         android:text="@string/upload" | ||||
|         /> | ||||
| 
 | ||||
|     <TextView | ||||
|         android:id="@+id/upload_progress" | ||||
|         style="@style/ProgressStyle" | ||||
|         android:layout_above="@id/frame" | ||||
|         android:layout_below="@id/list_view" | ||||
|         android:layout_toLeftOf="@+id/button_delete_remote" | ||||
|         android:layout_toRightOf="@id/button_upload" | ||||
|         android:gravity="center" | ||||
|         android:text="0%" | ||||
|         android:textSize="14sp" | ||||
|         /> | ||||
| 
 | ||||
|     <Button | ||||
|         android:id="@id/button_delete_remote" | ||||
|         style="@style/ButtonStyle" | ||||
|         android:layout_above="@id/frame" | ||||
|         android:layout_alignParentRight="true" | ||||
|         android:onClick="onClickHandler" | ||||
|         android:text="@string/delete_remote_file" | ||||
|         /> | ||||
| 
 | ||||
|     <FrameLayout | ||||
|         android:id="@id/frame" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="@dimen/frame_height" | ||||
|         android:layout_above="@+id/button_download" | ||||
|         android:layout_alignParentLeft="true" | ||||
|         android:layout_alignParentRight="true" | ||||
|         > | ||||
|     </FrameLayout> | ||||
| 
 | ||||
|     <Button | ||||
|         android:id="@id/button_download" | ||||
|         style="@style/ButtonStyle" | ||||
|         android:layout_alignParentBottom="true" | ||||
|         android:layout_alignParentLeft="true" | ||||
|         android:onClick="onClickHandler" | ||||
|         android:text="@string/download" | ||||
|         /> | ||||
| 
 | ||||
|     <TextView | ||||
|         android:id="@+id/download_progress" | ||||
|         style="@style/ProgressStyle" | ||||
|         android:layout_alignParentBottom="true" | ||||
|         android:layout_below="@id/frame" | ||||
|         android:layout_toLeftOf="@+id/button_delete_local" | ||||
|         android:layout_toRightOf="@id/button_download" | ||||
|         android:gravity="center" | ||||
|         android:text="0%" | ||||
|         android:textSize="14sp" | ||||
|         /> | ||||
| 
 | ||||
|     <Button | ||||
|         android:id="@id/button_delete_local" | ||||
|         style="@style/ButtonStyle" | ||||
|         android:layout_alignParentBottom="true" | ||||
|         android:layout_alignParentRight="true" | ||||
|         android:onClick="onClickHandler" | ||||
|         android:text="@string/delete_local_file" | ||||
|         /> | ||||
| 
 | ||||
| </RelativeLayout> | ||||
| 
 | ||||
| @ -1,39 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!--  ownCloud Android Library is available under MIT license | ||||
|    Copyright (C) 2016 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. | ||||
| 
 | ||||
|  --> | ||||
| 
 | ||||
| <resources xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
| 
 | ||||
|     <style name="ButtonStyle" parent="@android:style/Widget.Holo.Button"> | ||||
|         <item name="android:layout_width">120dp</item> | ||||
|         <item name="android:layout_height">wrap_content</item> | ||||
|         <item name="android:textSize">12sp</item> | ||||
|     </style> | ||||
| 
 | ||||
|     <style name="ProgressStyle" parent="@android:style/Widget.Holo.TextView"> | ||||
|         <item name="android:layout_width">wrap_content</item> | ||||
|         <item name="android:layout_height">wrap_content</item> | ||||
|         <item name="android:textSize">12sp</item> | ||||
|     </style> | ||||
| </resources> | ||||
| @ -1,28 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!--  ownCloud Android Library is available under MIT license | ||||
|    Copyright (C) 2016 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. | ||||
| 
 | ||||
|  --> | ||||
| 
 | ||||
| <resources> | ||||
|     <dimen name="frame_height">120dp</dimen> | ||||
| </resources> | ||||
| @ -1,30 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!--  ownCloud Android Library is available under MIT license | ||||
|    Copyright (C) 2020 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. | ||||
|  --> | ||||
| 
 | ||||
| <resources> | ||||
|     <string name="server_base_url"></string> | ||||
|     <string name="username"></string> | ||||
|     <string name="password"></string> | ||||
|     <string name="user_agent">Mozilla/5.0 (Android) ownCloud sample </string> | ||||
| </resources> | ||||
| @ -1,47 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!--  ownCloud Android Library is available under MIT license | ||||
|    Copyright (C) 2016 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. | ||||
| 
 | ||||
|  --> | ||||
| 
 | ||||
| <resources> | ||||
|     <string name="app_name">ownCloud Sample Client</string> | ||||
|     <string name="refresh">Refresh</string> | ||||
|     <string name="upload">Upload</string> | ||||
|     <string name="delete_remote_file">Delete remote file</string> | ||||
|     <string name="download">Download</string> | ||||
|     <string name="delete_local_file">Delete local file</string> | ||||
|     <string name="youre_doing_it_wrong">You\'re doing it wrong</string> | ||||
|     <string name="todo_start_refresh">TODO: start refresh</string> | ||||
|     <string name="todo_start_upload">TODO: start upload</string> | ||||
|     <string name="todo_start_remote_deletion">TODO: start remote deletion</string> | ||||
|     <string name="todo_start_download">TODO: start download</string> | ||||
|     <string name="todo_start_local_deletion">TODO: start local deletion</string> | ||||
|     <string name="todo_operation_finished_in_success">TODO: operation finished in success</string> | ||||
|     <string name="todo_operation_finished_in_fail">TODO: operation finished in fail</string> | ||||
|     <string name="upload_folder_path">to_upload</string> | ||||
|     <string name="download_folder_path">downloaded</string> | ||||
|     <string name="error_copying_sample_file">Sample file could not be saved in temporal folder; upload will not work</string> | ||||
|     <string name="sample_file_name">oc_sample.png</string> | ||||
|     <string name="sample_file_mimetype">image/png</string> | ||||
|     <string name="error_deleting_local_file">Downloaded file could not be deleted</string> | ||||
| </resources> | ||||
| @ -1,39 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!--  ownCloud Android Library is available under MIT license | ||||
|    Copyright (C) 2016 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. | ||||
| 
 | ||||
|  --> | ||||
| 
 | ||||
| <resources xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
| 
 | ||||
|     <style name="ButtonStyle" parent="@android:style/Widget.Button"> | ||||
|         <item name="android:layout_width">120dp</item> | ||||
|         <item name="android:layout_height">wrap_content</item> | ||||
|         <item name="android:textSize">12sp</item> | ||||
|     </style> | ||||
| 
 | ||||
|     <style name="ProgressStyle" parent="@android:style/Widget.TextView"> | ||||
|         <item name="android:layout_width">wrap_content</item> | ||||
|         <item name="android:layout_height">wrap_content</item> | ||||
|         <item name="android:textSize">12sp</item> | ||||
|     </style> | ||||
| </resources> | ||||
| @ -1,2 +1 @@ | ||||
| include ':owncloudComLibrary' | ||||
| include ':sample_client' | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user