From 39b1fce7f6c2755c756fb3b4915aa38852d58a3c Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 23 Aug 2021 12:50:15 +0200 Subject: [PATCH 01/23] init connectionvalidator --- .../android/lib/common/ConnectionValidator.kt | 12 ++++++++++++ .../owncloud/android/lib/common/OwnCloudClient.java | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt new file mode 100644 index 00000000..0860ddde --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -0,0 +1,12 @@ +package com.owncloud.android.lib.common + +import timber.log.Timber + +class ConnectionValidator ( + private val ocClient: OwnCloudClient + ) { + + fun dosomething() { + Timber.d(ocClient.toString()) + } +} \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 8d27c34a..d1bcda7e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -67,6 +67,7 @@ public class OwnCloudClient extends HttpClient { private Uri mBaseUri; private OwnCloudVersion mVersion = null; private OwnCloudAccount mAccount; + private ConnectionValidator mConnectionValidator; private SingleSessionManager mSingleSessionManager = null; @@ -83,6 +84,7 @@ public class OwnCloudClient extends HttpClient { clearCredentials(); clearCookies(); + mConnectionValidator = new ConnectionValidator(this); } public void clearCredentials() { From 2f952a3a09fd67064c33d4b855d2c87a3c88a945 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 24 Aug 2021 15:47:24 +0200 Subject: [PATCH 02/23] debug and add mutex for halding requiests --- owncloudComLibrary/build.gradle | 1 + .../android/lib/common/OwnCloudClient.java | 42 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/owncloudComLibrary/build.gradle b/owncloudComLibrary/build.gradle index ba330838..e9f20869 100644 --- a/owncloudComLibrary/build.gradle +++ b/owncloudComLibrary/build.gradle @@ -12,6 +12,7 @@ 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' diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index d1bcda7e..bca78b1f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -28,6 +28,7 @@ package com.owncloud.android.lib.common; import android.accounts.AccountManager; import android.accounts.AccountsException; import android.net.Uri; +import android.util.Log; import at.bitfire.dav4jvm.exception.HttpException; import com.owncloud.android.lib.common.accounts.AccountUtils; @@ -42,6 +43,7 @@ import com.owncloud.android.lib.common.utils.RandomUtils; import com.owncloud.android.lib.resources.status.OwnCloudVersion; import okhttp3.Cookie; import okhttp3.HttpUrl; +import org.apache.commons.lang3.exception.ExceptionUtils; import timber.log.Timber; import java.io.IOException; @@ -69,6 +71,8 @@ public class OwnCloudClient extends HttpClient { private OwnCloudAccount mAccount; private ConnectionValidator mConnectionValidator; + private static Boolean mHoldRequests = false; + private SingleSessionManager mSingleSessionManager = null; private boolean mFollowRedirects; @@ -109,7 +113,41 @@ public class OwnCloudClient extends HttpClient { if (mCredentials.getHeaderAuth() != null && method.getRequestHeader(AUTHORIZATION_HEADER) == null) { method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); } - status = method.execute(); + synchronized (mHoldRequests) { + while (mHoldRequests) { + while (true) { + try { + ((String) null).toString(); + } catch (Exception e) { + Log.d("+++++++", + "HATL BEFORE" + + "\nThread: " + Thread.currentThread().getName() + + "\nobject: " + this.toString() + + "\nMethod: " + method.getHttpUrl() + + "\ntrace: " + ExceptionUtils.getStackTrace(e)); + } + Thread.sleep(40000); + } + } + status = method.execute(); + if (status == 302) { + mHoldRequests = true; + while (mHoldRequests) { + try { + ((String) null).toString(); + } catch (Exception e) { + Log.d("+++++++", + "HALT AFTER" + + "\nresponsecode: " + Integer.toString(status) + + "\nThread: " + Thread.currentThread().getName() + + "\nobject: " + this.toString() + + "\nMethod: " + method.getHttpUrl() + + "\ntrace: " + ExceptionUtils.getStackTrace(e)); + } + Thread.sleep(40000); + } + } + } if (mFollowRedirects) { status = followRedirection(method).getLastStatus(); @@ -171,7 +209,7 @@ public class OwnCloudClient extends HttpClient { redirectionPath.addLocation(location); - // Release the connection to avoid reach the max number of connections per host + // Release the connection to avoid reach the max number of connections per hostClientManager // due to it will be set a different url exhaustResponse(method.getResponseBodyAsStream()); From 0d94058db912f1056eeb6405f741b6a29b763ab1 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Wed, 1 Sep 2021 17:34:01 +0200 Subject: [PATCH 03/23] remove retrive cookies from middleware --- .../android/lib/common/OwnCloudClient.java | 32 +++++++++++++++---- .../lib/common/OwnCloudClientFactory.java | 8 ----- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index bca78b1f..50dbecf7 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -38,6 +38,7 @@ 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.http.methods.nonwebdav.HttpMethod; import com.owncloud.android.lib.common.network.RedirectionPath; import com.owncloud.android.lib.common.utils.RandomUtils; import com.owncloud.android.lib.resources.status.OwnCloudVersion; @@ -113,14 +114,17 @@ public class OwnCloudClient extends HttpClient { if (mCredentials.getHeaderAuth() != null && method.getRequestHeader(AUTHORIZATION_HEADER) == null) { method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); } + + status = method.execute(); + stacklog(status, method); + /* synchronized (mHoldRequests) { while (mHoldRequests) { while (true) { try { - ((String) null).toString(); + throw new Exception("Stack log"); } catch (Exception e) { - Log.d("+++++++", - "HATL BEFORE" + + Timber.d( "HATL BEFORE" + "\nThread: " + Thread.currentThread().getName() + "\nobject: " + this.toString() + "\nMethod: " + method.getHttpUrl() + @@ -134,10 +138,9 @@ public class OwnCloudClient extends HttpClient { mHoldRequests = true; while (mHoldRequests) { try { - ((String) null).toString(); + throw new Exception("Stack log"); } catch (Exception e) { - Log.d("+++++++", - "HALT AFTER" + + Timber.d( "HALT AFTER" + "\nresponsecode: " + Integer.toString(status) + "\nThread: " + Thread.currentThread().getName() + "\nobject: " + this.toString() + @@ -147,7 +150,9 @@ public class OwnCloudClient extends HttpClient { Thread.sleep(40000); } } + } + */ if (mFollowRedirects) { status = followRedirection(method).getLastStatus(); @@ -162,6 +167,21 @@ public class OwnCloudClient extends HttpClient { return status; } + private void stacklog(int status, HttpBaseMethod method) { + try { + throw new Exception("Stack log"); + } catch(Exception e) { + Timber.d("\n---------------------------" + + "\nresponsecode: " + status + + "\nThread: " + Thread.currentThread().getName() + + "\nobject: " + this.toString() + + "\nMethod: " + method.toString() + + "\nUrl: " + method.getHttpUrl() + + "\ntrace: " + ExceptionUtils.getStackTrace(e) + + "---------------------------"); + } + } + private int executeRedirectedHttpMethod(HttpBaseMethod method) throws Exception { boolean repeatWithFreshCredentials; int repeatCounter = 0; diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java index 948e8a07..f9f0dbfa 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java @@ -42,17 +42,9 @@ public class OwnCloudClientFactory { */ 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); - } } From 0e82f983b5cfd8b6e90b0d4c7361a4493a057f06 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Thu, 2 Sep 2021 11:15:16 +0200 Subject: [PATCH 04/23] remove OwnCloudClient factory --- .../lib/common/OwnCloudClientFactory.java | 50 ------------------- .../lib/common/SingleSessionManager.java | 10 +++- 2 files changed, 9 insertions(+), 51 deletions(-) delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java deleted file mode 100644 index f9f0dbfa..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java +++ /dev/null @@ -1,50 +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.common; - -import android.content.Context; -import android.net.Uri; - -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); - - return client; - } -} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java index c410cc90..25aebc6e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java @@ -68,6 +68,14 @@ public class SingleSessionManager { sUserAgent = userAgent; } + private static OwnCloudClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) { + OwnCloudClient client = new OwnCloudClient(uri); + client.setFollowRedirects(followRedirects); + HttpClient.setContext(context); + + return client; + } + public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws OperationCanceledException, AuthenticatorException, IOException { @@ -104,7 +112,7 @@ 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 From 7e4b43e7cb4da83ebf65be7d5945761426297303 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 6 Sep 2021 12:09:21 +0200 Subject: [PATCH 05/23] prepare code for the inclusion of connection validator --- .../android/lib/common/ConnectionValidator.kt | 10 +- .../android/lib/common/OwnCloudClient.java | 96 ++++++++----------- .../lib/common/SingleSessionManager.java | 26 ++++- .../common/operations/RemoteOperation.java | 2 +- 4 files changed, 67 insertions(+), 67 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index 0860ddde..e4ee8483 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -1,12 +1,12 @@ package com.owncloud.android.lib.common +import com.owncloud.android.lib.common.http.methods.HttpBaseMethod +import com.owncloud.android.lib.common.http.methods.nonwebdav.HttpMethod import timber.log.Timber -class ConnectionValidator ( - private val ocClient: OwnCloudClient - ) { +class ConnectionValidator { - fun dosomething() { - Timber.d(ocClient.toString()) + fun validate(method: HttpBaseMethod, client: OwnCloudClient) { + Timber.d("hello world") } } \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 50dbecf7..5941f9e9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -28,7 +28,6 @@ package com.owncloud.android.lib.common; import android.accounts.AccountManager; import android.accounts.AccountsException; import android.net.Uri; -import android.util.Log; import at.bitfire.dav4jvm.exception.HttpException; import com.owncloud.android.lib.common.accounts.AccountUtils; @@ -38,7 +37,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.http.methods.nonwebdav.HttpMethod; import com.owncloud.android.lib.common.network.RedirectionPath; import com.owncloud.android.lib.common.utils.RandomUtils; import com.owncloud.android.lib.resources.status.OwnCloudVersion; @@ -70,26 +68,32 @@ public class OwnCloudClient extends HttpClient { private Uri mBaseUri; private OwnCloudVersion mVersion = null; private OwnCloudAccount mAccount; - private ConnectionValidator mConnectionValidator; + private final ConnectionValidator mConnectionValidator; + private Object mRequestMutex = new Object(); - private static Boolean mHoldRequests = false; + // 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; - public OwnCloudClient(Uri baseUri) { + public OwnCloudClient(Uri baseUri, ConnectionValidator connectionValidator, boolean synchronizeRequests) { if (baseUri == null) { throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL"); } mBaseUri = baseUri; + mSynchronizeRequests = synchronizeRequests; mInstanceNumber = sIntanceCounter++; Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient"); clearCredentials(); clearCookies(); - mConnectionValidator = new ConnectionValidator(this); + mConnectionValidator = connectionValidator; } public void clearCredentials() { @@ -99,10 +103,21 @@ public class OwnCloudClient extends HttpClient { } public int executeHttpMethod(HttpBaseMethod method) throws Exception { + if(mSynchronizeRequests) { + synchronized (mRequestMutex) { + return saveExecuteHttpMethod(method); + } + } else { + return saveExecuteHttpMethod(method); + } + } + + private int saveExecuteHttpMethod(HttpBaseMethod method) throws Exception { boolean repeatWithFreshCredentials; int repeatCounter = 0; int status; + boolean retry = false; do { String requestId = RandomUtils.generateRandomUUID(); @@ -117,44 +132,14 @@ public class OwnCloudClient extends HttpClient { status = method.execute(); stacklog(status, method); - /* - synchronized (mHoldRequests) { - while (mHoldRequests) { - while (true) { - try { - throw new Exception("Stack log"); - } catch (Exception e) { - Timber.d( "HATL BEFORE" + - "\nThread: " + Thread.currentThread().getName() + - "\nobject: " + this.toString() + - "\nMethod: " + method.getHttpUrl() + - "\ntrace: " + ExceptionUtils.getStackTrace(e)); - } - Thread.sleep(40000); - } - } - status = method.execute(); - if (status == 302) { - mHoldRequests = true; - while (mHoldRequests) { - try { - throw new Exception("Stack log"); - } catch (Exception e) { - Timber.d( "HALT AFTER" + - "\nresponsecode: " + Integer.toString(status) + - "\nThread: " + Thread.currentThread().getName() + - "\nobject: " + this.toString() + - "\nMethod: " + method.getHttpUrl() + - "\ntrace: " + ExceptionUtils.getStackTrace(e)); - } - Thread.sleep(40000); - } - } + if (status == HttpConstants.HTTP_MOVED_TEMPORARILY) { + mConnectionValidator.validate(method, this); + retry = true; } - */ if (mFollowRedirects) { + status = followRedirection(method).getLastStatus(); } @@ -163,6 +148,7 @@ public class OwnCloudClient extends HttpClient { repeatCounter++; } } while (repeatWithFreshCredentials); +// } while (retry); return status; } @@ -384,22 +370,20 @@ public class OwnCloudClient extends HttpClient { boolean credentialsWereRefreshed = false; if (shouldInvalidateAccountCredentials(status)) { - boolean invalidated = invalidateAccountCredentials(); + 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; + 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 - ); - } + } catch (AccountsException | IOException e) { + Timber.e(e, "Error while trying to refresh auth token for %s", + mAccount.getSavedAccount().name + ); } if (!credentialsWereRefreshed && mSingleSessionManager != null) { @@ -441,16 +425,14 @@ public class OwnCloudClient extends HttpClient { *

* {@link #shouldInvalidateAccountCredentials(int)} should be called first. * - * @return 'True' if invalidation was successful, 'false' otherwise. */ - private boolean invalidateAccountCredentials() { + private void 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() { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java index 25aebc6e..225c48b0 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java @@ -49,6 +49,7 @@ public class SingleSessionManager { private static SingleSessionManager sDefaultSingleton; private static String sUserAgent; + private static ConnectionValidator sConnectionValidator; private ConcurrentMap mClientsWithKnownUsername = new ConcurrentHashMap<>(); private ConcurrentMap 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,15 +77,23 @@ public class SingleSessionManager { sUserAgent = userAgent; } - private static OwnCloudClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) { - OwnCloudClient client = new OwnCloudClient(uri); + private static OwnCloudClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects, ConnectionValidator connectionValidator) { + OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true); client.setFollowRedirects(followRedirects); HttpClient.setContext(context); return client; } - public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws OperationCanceledException, + 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 "); @@ -115,7 +132,8 @@ public class SingleSessionManager { client = createOwnCloudClient( account.getBaseUri(), context.getApplicationContext(), - true); // TODO remove dependency on OwnCloudClientFactory + true, + connectionValidator); // 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 diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperation.java index c866b277..637aa6be 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperation.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperation.java @@ -159,7 +159,7 @@ public abstract class RemoteOperation 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)"); From ca206173dfddc8b836b715478ec567ce648f44d5 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 7 Sep 2021 16:37:02 +0200 Subject: [PATCH 06/23] create logic scaffold for connection validator get status.php with it --- .../android/lib/common/ConnectionValidator.kt | 88 ++++++++++++++++++- .../android/lib/common/OwnCloudClient.java | 3 +- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index e4ee8483..4ac6009d 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -1,12 +1,94 @@ package com.owncloud.android.lib.common +import com.owncloud.android.lib.common.authentication.OwnCloudCredentials +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory +import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.methods.HttpBaseMethod -import com.owncloud.android.lib.common.http.methods.nonwebdav.HttpMethod +import com.owncloud.android.lib.common.operations.RemoteOperationResult +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.lang.Exception class ConnectionValidator { - fun validate(method: HttpBaseMethod, client: OwnCloudClient) { - Timber.d("hello world") + fun validate(method: HttpBaseMethod, baseClient: OwnCloudClient): Boolean { + try { + var validationRetryCount = 0 + val client = OwnCloudClient(baseClient.baseUri, null, false) + client.credentials = baseClient.credentials + client.setFollowRedirects(true) + while (validationRetryCount < 5) { + var successCounter = 0 + var failCounter = 0 + + if (isOnwCloudStatusOk(client)) { + successCounter++ + } else { + failCounter++ + } + + val contentReply = accessRootFolder() + if (contentReply == HttpConstants.HTTP_OK) { + if (isRootFolderOk(contentReply)) { + successCounter++ + } else { + failCounter++ + } + } else { + failCounter++ + if (contentReply == HttpConstants.HTTP_UNAUTHORIZED) { + triggerAuthRefresh() + } + } + if(successCounter >= failCounter) { + //update credentials in client + 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 { + //TODO: Implement me + val reply = getOwnCloudStatus(client) + return if (reply.httpCode == HttpConstants.HTTP_OK) { + isOCStatusReplyValid(reply.data) + } else { + false + } + } + + private fun getOwnCloudStatus(client: OwnCloudClient): RemoteOperationResult { + val remoteStatusOperation = GetRemoteStatusOperation() + //TODO: follow redirects only 5 times + return remoteStatusOperation.execute(client) + } + + private fun isOCStatusReplyValid(info: RemoteServerInfo): Boolean { + //TODO: Implement me + Timber.d("owncloud version %s", info.ownCloudVersion.version) + return true + } + + private fun triggerAuthRefresh(): OwnCloudCredentials { + //TODO: Implement me + return OwnCloudCredentialsFactory.getAnonymousCredentials() + } + + private fun accessRootFolder(): Int { + //TODO: Implement me + return 55 + } + + private fun isRootFolderOk(content: Int): Boolean { + //TODO: Implement me + return true } } \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 5941f9e9..2a0152ba 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -133,7 +133,8 @@ public class OwnCloudClient extends HttpClient { status = method.execute(); stacklog(status, method); - if (status == HttpConstants.HTTP_MOVED_TEMPORARILY) { + if (mConnectionValidator != null && + status == HttpConstants.HTTP_MOVED_TEMPORARILY) { mConnectionValidator.validate(method, this); retry = true; } From c2c351c912123ea4fbc0c266b7948929f17156f6 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 7 Sep 2021 16:42:12 +0200 Subject: [PATCH 07/23] set max redirect count for owncloud client to 5 --- .../java/com/owncloud/android/lib/common/OwnCloudClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 2a0152ba..2c0eb111 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -58,7 +58,7 @@ public class OwnCloudClient extends HttpClient { 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_REDIRECTIONS_COUNT = 5; private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1; private static byte[] sExhaustBuffer = new byte[1024]; From ddb15a33f17bf9b483665f87439b7f13794edfa2 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 7 Sep 2021 17:14:16 +0200 Subject: [PATCH 08/23] try shifting over to connection validator for updating credentials intermediate commit --- .../android/lib/common/ConnectionValidator.kt | 11 +++++- .../android/lib/common/OwnCloudClient.java | 35 ++++++++++++++----- .../operations/RemoteOperationResult.java | 11 +++++- .../status/GetRemoteStatusOperation.kt | 13 +++---- .../lib/resources/status/StatusRequester.kt | 28 +++------------ 5 files changed, 58 insertions(+), 40 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index 4ac6009d..fa488b1b 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -11,12 +11,21 @@ import org.apache.commons.lang3.exception.ExceptionUtils import timber.log.Timber import java.lang.Exception -class ConnectionValidator { +class ConnectionValidator ( + val clearCookiesOnValidation: Boolean + ){ fun validate(method: HttpBaseMethod, baseClient: OwnCloudClient): Boolean { try { var validationRetryCount = 0 val client = OwnCloudClient(baseClient.baseUri, null, false) + if (clearCookiesOnValidation) { + client.cookiesForBaseUri = emptyList() + } else { + client.cookiesForBaseUri = baseClient.cookiesForBaseUri + } + //TODO: Also handle cookies + client.credentials = baseClient.credentials client.setFollowRedirects(true) while (validationRetryCount < 5) { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 2c0eb111..db3a78d4 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -47,6 +47,7 @@ import timber.log.Timber; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER; @@ -131,24 +132,25 @@ public class OwnCloudClient extends HttpClient { } status = method.execute(); + Timber.d("-------------------------------------"); stacklog(status, method); if (mConnectionValidator != null && status == HttpConstants.HTTP_MOVED_TEMPORARILY) { mConnectionValidator.validate(method, this); retry = true; - } - - if (mFollowRedirects) { - + } else if (mFollowRedirects) { status = followRedirection(method).getLastStatus(); } + /* repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter); if (repeatWithFreshCredentials) { repeatCounter++; } - } while (repeatWithFreshCredentials); + + */ + } while (retry); // } while (retry); return status; @@ -164,6 +166,7 @@ public class OwnCloudClient extends HttpClient { "\nobject: " + this.toString() + "\nMethod: " + method.toString() + "\nUrl: " + method.getHttpUrl() + + "\nCookeis: " + getCookiesString() + "\ntrace: " + ExceptionUtils.getStackTrace(e) + "---------------------------"); } @@ -220,7 +223,8 @@ public class OwnCloudClient extends HttpClient { // due to it will be set a different url exhaustResponse(method.getResponseBodyAsStream()); - method.setUrl(HttpUrl.parse(location)); + Timber.d("+++++++++++++++++++++++++++++++++++++++ %s", getFullUrl(location)); + method.setUrl(getFullUrl(location)); final String destination = method.getRequestHeader("Destination") != null ? method.getRequestHeader("Destination") : method.getRequestHeader("destination"); @@ -252,6 +256,14 @@ public class OwnCloudClient extends HttpClient { return redirectionPath; } + private HttpUrl getFullUrl(String redirection) { + if(redirection.startsWith("/")) { + return HttpUrl.parse(mBaseUri.toString() + redirection); + } else { + return HttpUrl.parse(redirection); + } + } + /** * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation. * @@ -322,7 +334,7 @@ public class OwnCloudClient extends HttpClient { public String getCookiesString() { StringBuilder cookiesString = new StringBuilder(); - List cookieList = getCookiesFromUrl(HttpUrl.parse(mBaseUri.toString())); + List cookieList = getCookiesForBaseUri(); if (cookieList != null) { for (Cookie cookie : cookieList) { @@ -333,13 +345,18 @@ public class OwnCloudClient extends HttpClient { return cookiesString.toString(); } - public void setCookiesForCurrentAccount(List cookies) { + public void setCookiesForBaseUri(List cookies) { getOkHttpClient().cookieJar().saveFromResponse( - HttpUrl.parse(getAccount().getBaseUri().toString()), + HttpUrl.parse(mBaseUri.toString()), cookies ); } + public List getCookiesForBaseUri() { + return getOkHttpClient().cookieJar().loadForRequest( + HttpUrl.parse(mBaseUri.toString())); + } + public OwnCloudVersion getOwnCloudVersion() { return mVersion; } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java index 6e9e7e60..4f707821 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java @@ -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; @@ -112,6 +113,14 @@ public class RemoteOperationResult */ 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; @@ -321,7 +330,7 @@ public class RemoteOperationResult 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) } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt index 380ca3dc..21f02da0 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt @@ -45,27 +45,28 @@ import timber.log.Timber class GetRemoteStatusOperation : RemoteOperation() { public override fun run(client: OwnCloudClient): RemoteOperationResult { - client.baseUri = buildFullHttpsUrl(client.baseUri) + 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 result } private fun tryToConnect(client: OwnCloudClient): RemoteOperationResult { 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) + requester.handleRequestResult(requestResult, baseUrl) } catch (e: JSONException) { RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED) } catch (e: Exception) { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt index f25086f3..e1fdb441 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt @@ -47,7 +47,7 @@ internal class StatusRequester { redirectedUrl: String ) = redirectedToNonSecureLocationBefore || (baseUrl.startsWith(HTTPS_SCHEME) && - !redirectedUrl.startsWith(HTTPS_SCHEME)) + !redirectedUrl.startsWith(HTTPS_SCHEME)) fun updateLocationWithRedirectPath(oldLocation: String, redirectedLocation: String): String { /** Redirection with different endpoint. @@ -77,32 +77,14 @@ internal class StatusRequester { val lastLocation: String ) - fun requestAndFollowRedirects(baseLocation: String, client: OwnCloudClient): RequestResult { + fun request(baseLocation: String, client: OwnCloudClient): RequestResult { var currentLocation = baseLocation + OwnCloudClient.STATUS_PATH var redirectedToUnsecureLocation = false var status: Int - while (true) { - val getMethod = getGetMethod(currentLocation) - - status = client.executeHttpMethod(getMethod) - val result = - if (status.isSuccess()) RemoteOperationResult(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 - } - } + val getMethod = getGetMethod(currentLocation) + status = client.executeHttpMethod(getMethod) + return RequestResult(getMethod, status, redirectedToUnsecureLocation, currentLocation) } private fun Int.isSuccess() = this == HttpConstants.HTTP_OK From 8d09a5c242d4f6f57d95cc120f869b0da1dfa9eb Mon Sep 17 00:00:00 2001 From: Schabi Date: Fri, 10 Sep 2021 10:44:06 +0200 Subject: [PATCH 09/23] make initial connection using connection validator work --- .../java/com/owncloud/android/lib/common/OwnCloudClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index db3a78d4..09c977d6 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -120,6 +120,7 @@ public class OwnCloudClient extends HttpClient { boolean retry = false; do { + retry = false; String requestId = RandomUtils.generateRandomUUID(); // Header to allow tracing requests in apache and ownCloud logs @@ -151,7 +152,6 @@ public class OwnCloudClient extends HttpClient { */ } while (retry); -// } while (retry); return status; } From e878ad39313d6c37d738217886aabd9f0a3f5b4b Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Fri, 10 Sep 2021 13:02:09 +0200 Subject: [PATCH 10/23] make oidc discovery work again --- .../android/lib/common/ConnectionValidator.kt | 3 +-- .../android/lib/common/OwnCloudClient.java | 9 ++++++--- .../android/lib/common/http/HttpClient.java | 16 ++-------------- .../files/CheckPathExistenceRemoteOperation.kt | 3 +-- .../files/ReadRemoteFolderOperation.java | 2 -- .../oauth/GetOIDCDiscoveryRemoteOperation.kt | 1 + 6 files changed, 11 insertions(+), 23 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index fa488b1b..045bb990 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -20,11 +20,10 @@ class ConnectionValidator ( var validationRetryCount = 0 val client = OwnCloudClient(baseClient.baseUri, null, false) if (clearCookiesOnValidation) { - client.cookiesForBaseUri = emptyList() + client.clearCookies() } else { client.cookiesForBaseUri = baseClient.cookiesForBaseUri } - //TODO: Also handle cookies client.credentials = baseClient.credentials client.setFollowRedirects(true) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 09c977d6..39b2c8f2 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -62,7 +62,6 @@ public class OwnCloudClient extends HttpClient { private static final int MAX_REDIRECTIONS_COUNT = 5; private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1; - private static byte[] sExhaustBuffer = new byte[1024]; private static int sIntanceCounter = 0; private OwnCloudCredentials mCredentials = null; private int mInstanceNumber; @@ -80,7 +79,7 @@ public class OwnCloudClient extends HttpClient { private SingleSessionManager mSingleSessionManager = null; - private boolean mFollowRedirects; + private boolean mFollowRedirects = false; public OwnCloudClient(Uri baseUri, ConnectionValidator connectionValidator, boolean synchronizeRequests) { if (baseUri == null) { @@ -357,6 +356,10 @@ public class OwnCloudClient extends HttpClient { HttpUrl.parse(mBaseUri.toString())); } + public void clearCookies() { + setCookiesForBaseUri(new ArrayList<>()); + } + public OwnCloudVersion getOwnCloudVersion() { return mVersion; } @@ -453,7 +456,7 @@ public class OwnCloudClient extends HttpClient { am.clearPassword(mAccount.getSavedAccount()); // being strict, only needed for Basic Auth credentials } - public boolean followRedirects() { + public boolean getFollowRedirects() { return mFollowRedirects; } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java index ed1dd847..910e0936 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java @@ -57,7 +57,6 @@ import java.util.concurrent.TimeUnit; public class HttpClient { private static OkHttpClient sOkHttpClient; private static Context sContext; - private static HashMap> sCookieStore = new HashMap<>(); private static LogInterceptor sLogInterceptor; private static Interceptor sDebugInterceptor; @@ -68,11 +67,10 @@ public class HttpClient { NetworkUtils.getKnownServersStore(sContext)); final SSLSocketFactory sslSocketFactory = getNewSslSocketFactory(trustManager); // 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); + sOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager); } catch (Exception e) { Timber.e(e, "Could not setup SSL system."); @@ -109,8 +107,7 @@ public class HttpClient { return sslContext.getSocketFactory(); } - private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager, - CookieJar cookieJar) { + private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager){ return new OkHttpClient.Builder() .addNetworkInterceptor(getLogInterceptor()) .addNetworkInterceptor(DebugInterceptorFactory.INSTANCE.getInterceptor()) @@ -121,7 +118,6 @@ public class HttpClient { .followRedirects(false) .sslSocketFactory(sslSocketFactory, trustManager) .hostnameVerifier((asdf, usdf) -> true) - .cookieJar(cookieJar) .build(); } @@ -132,10 +128,6 @@ public class HttpClient { return sLogInterceptor; } - public static List getCookiesFromUrl(HttpUrl httpUrl) { - return sCookieStore.get(httpUrl.host()); - } - public Context getContext() { return sContext; } @@ -143,8 +135,4 @@ public class HttpClient { public static void setContext(Context context) { sContext = context; } - - public void clearCookies() { - sCookieStore.clear(); - } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt index f3da9a72..6106c74f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt @@ -60,7 +60,7 @@ class CheckPathExistenceRemoteOperation( private set override fun run(client: OwnCloudClient): RemoteOperationResult { - val previousFollowRedirects = client.followRedirects() + val previousFollowRedirects = client.getFollowRedirects() return try { val stringUrl = if (isUserLogged) client.baseFilesWebDavUri.toString() @@ -71,7 +71,6 @@ class CheckPathExistenceRemoteOperation( setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) } - client.setFollowRedirects(false) var status = client.executeHttpMethod(propFindMethod) if (previousFollowRedirects) { redirectionPath = client.followRedirection(propFindMethod) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java index 03cb3aa7..e10613b3 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java @@ -77,8 +77,6 @@ public class ReadRemoteFolderOperation extends RemoteOperation() addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) } + getMethod.setFollowRedirects(true) val status = client.executeHttpMethod(getMethod) val responseBody = getMethod.getResponseBodyAsString() From 7ccb86c153aab03a9f547584ebed0e485a2341af Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Fri, 10 Sep 2021 15:54:49 +0200 Subject: [PATCH 11/23] stop validation process if failing --- .../android/lib/common/ConnectionValidator.kt | 41 +++++++------------ .../android/lib/common/OwnCloudClient.java | 6 +-- .../CheckPathExistenceRemoteOperation.kt | 5 --- 3 files changed, 16 insertions(+), 36 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index 045bb990..3284a482 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -5,6 +5,7 @@ import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.methods.HttpBaseMethod 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 @@ -15,7 +16,7 @@ class ConnectionValidator ( val clearCookiesOnValidation: Boolean ){ - fun validate(method: HttpBaseMethod, baseClient: OwnCloudClient): Boolean { + fun validate(baseClient: OwnCloudClient): Boolean { try { var validationRetryCount = 0 val client = OwnCloudClient(baseClient.baseUri, null, false) @@ -26,27 +27,28 @@ class ConnectionValidator ( } client.credentials = baseClient.credentials - client.setFollowRedirects(true) while (validationRetryCount < 5) { var successCounter = 0 var failCounter = 0 + client.setFollowRedirects(true) if (isOnwCloudStatusOk(client)) { successCounter++ } else { failCounter++ } - val contentReply = accessRootFolder() - if (contentReply == HttpConstants.HTTP_OK) { - if (isRootFolderOk(contentReply)) { + 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 == HttpConstants.HTTP_UNAUTHORIZED) { + if (contentReply.hashCode() == HttpConstants.HTTP_UNAUTHORIZED) { triggerAuthRefresh() } } @@ -64,39 +66,24 @@ class ConnectionValidator ( } private fun isOnwCloudStatusOk(client: OwnCloudClient): Boolean { - //TODO: Implement me val reply = getOwnCloudStatus(client) - return if (reply.httpCode == HttpConstants.HTTP_OK) { - isOCStatusReplyValid(reply.data) - } else { - false - } + return reply.httpCode == HttpConstants.HTTP_OK && + !reply.isException && + reply.data != null } private fun getOwnCloudStatus(client: OwnCloudClient): RemoteOperationResult { val remoteStatusOperation = GetRemoteStatusOperation() - //TODO: follow redirects only 5 times return remoteStatusOperation.execute(client) } - private fun isOCStatusReplyValid(info: RemoteServerInfo): Boolean { - //TODO: Implement me - Timber.d("owncloud version %s", info.ownCloudVersion.version) - return true - } - private fun triggerAuthRefresh(): OwnCloudCredentials { //TODO: Implement me return OwnCloudCredentialsFactory.getAnonymousCredentials() } - private fun accessRootFolder(): Int { - //TODO: Implement me - return 55 - } - - private fun isRootFolderOk(content: Int): Boolean { - //TODO: Implement me - return true + private fun canAccessRootFolder(client: OwnCloudClient): RemoteOperationResult { + val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation("/", true) + return checkPathExistenceRemoteOperation.execute(client) } } \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 39b2c8f2..b3aeac12 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -56,7 +56,6 @@ import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID 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 = 5; @@ -137,8 +136,7 @@ public class OwnCloudClient extends HttpClient { if (mConnectionValidator != null && status == HttpConstants.HTTP_MOVED_TEMPORARILY) { - mConnectionValidator.validate(method, this); - retry = true; + retry = mConnectionValidator.validate(this); // retry on success fail on no success } else if (mFollowRedirects) { status = followRedirection(method).getLastStatus(); } @@ -463,4 +461,4 @@ public class OwnCloudClient extends HttpClient { public void setFollowRedirects(boolean followRedirects) { this.mFollowRedirects = followRedirects; } -} +} \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt index 6106c74f..5e0d974c 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt @@ -98,11 +98,6 @@ class CheckPathExistenceRemoteOperation( } } - /** - * @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 { From b2f6d7f3b15d361578774f0885b0c769c612229d Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Fri, 10 Sep 2021 16:39:02 +0200 Subject: [PATCH 12/23] make initial check with apm work though connection validator --- .../android/lib/common/ConnectionValidator.kt | 34 +++++++++++-------- .../android/lib/common/OwnCloudClient.java | 16 +-------- .../android/lib/common/http/HttpClient.java | 8 +++-- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index 3284a482..a052924f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -14,7 +14,7 @@ import java.lang.Exception class ConnectionValidator ( val clearCookiesOnValidation: Boolean - ){ +){ fun validate(baseClient: OwnCloudClient): Boolean { try { @@ -28,6 +28,7 @@ class ConnectionValidator ( client.credentials = baseClient.credentials while (validationRetryCount < 5) { + Timber.d("+++++++++++++++++++++++++++++++++++++ validationRetryCout %d", validationRetryCount) var successCounter = 0 var failCounter = 0 @@ -38,21 +39,24 @@ class ConnectionValidator ( failCounter++ } - 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++ + // 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 OwnCloudCredentialsFactory.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++ - } - } else { - failCounter++ - if (contentReply.hashCode() == HttpConstants.HTTP_UNAUTHORIZED) { - triggerAuthRefresh() + if (contentReply.hashCode() == HttpConstants.HTTP_UNAUTHORIZED) { + triggerAuthRefresh() + } } } - if(successCounter >= failCounter) { + if (successCounter >= failCounter) { //update credentials in client return true } @@ -67,8 +71,10 @@ class ConnectionValidator ( private fun isOnwCloudStatusOk(client: OwnCloudClient): Boolean { val reply = getOwnCloudStatus(client) - return reply.httpCode == HttpConstants.HTTP_OK && - !reply.isException && + // 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 } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index b3aeac12..eb3432b2 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -131,7 +131,6 @@ public class OwnCloudClient extends HttpClient { } status = method.execute(); - Timber.d("-------------------------------------"); stacklog(status, method); if (mConnectionValidator != null && @@ -163,7 +162,7 @@ public class OwnCloudClient extends HttpClient { "\nobject: " + this.toString() + "\nMethod: " + method.toString() + "\nUrl: " + method.getHttpUrl() + - "\nCookeis: " + getCookiesString() + + "\nCookeis: " + getCookiesForBaseUri().toString() + "\ntrace: " + ExceptionUtils.getStackTrace(e) + "---------------------------"); } @@ -329,19 +328,6 @@ public class OwnCloudClient extends HttpClient { } } - public String getCookiesString() { - StringBuilder cookiesString = new StringBuilder(); - List cookieList = getCookiesForBaseUri(); - - if (cookieList != null) { - for (Cookie cookie : cookieList) { - cookiesString.append(cookie.toString()).append(";"); - } - } - - return cookiesString.toString(); - } - public void setCookiesForBaseUri(List cookies) { getOkHttpClient().cookieJar().saveFromResponse( HttpUrl.parse(mBaseUri.toString()), diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java index 910e0936..b231ffed 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java @@ -57,6 +57,7 @@ import java.util.concurrent.TimeUnit; public class HttpClient { private static OkHttpClient sOkHttpClient; private static Context sContext; + private static HashMap> sCookieStore = new HashMap<>(); private static LogInterceptor sLogInterceptor; private static Interceptor sDebugInterceptor; @@ -67,10 +68,11 @@ public class HttpClient { NetworkUtils.getKnownServersStore(sContext)); final SSLSocketFactory sslSocketFactory = getNewSslSocketFactory(trustManager); // 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); + sOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar); } catch (Exception e) { Timber.e(e, "Could not setup SSL system."); @@ -107,7 +109,8 @@ public class HttpClient { return sslContext.getSocketFactory(); } - private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager){ + private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager, + CookieJar cookieJar) { return new OkHttpClient.Builder() .addNetworkInterceptor(getLogInterceptor()) .addNetworkInterceptor(DebugInterceptorFactory.INSTANCE.getInterceptor()) @@ -118,6 +121,7 @@ public class HttpClient { .followRedirects(false) .sslSocketFactory(sslSocketFactory, trustManager) .hostnameVerifier((asdf, usdf) -> true) + .cookieJar(cookieJar) .build(); } From cfd69987e97932428c779e82c25fe528154c22c9 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 13 Sep 2021 15:24:33 +0200 Subject: [PATCH 13/23] update session from APM while running the app --- owncloudComLibrary/build.gradle | 2 ++ .../android/lib/common/ConnectionValidator.kt | 4 ++-- .../owncloud/android/lib/common/OwnCloudClient.java | 4 ---- .../owncloud/android/lib/common/http/CookieJarImpl.kt | 11 +++++------ .../owncloud/android/lib/common/http/HttpClient.java | 5 +++++ 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/owncloudComLibrary/build.gradle b/owncloudComLibrary/build.gradle index e9f20869..c98d293f 100644 --- a/owncloudComLibrary/build.gradle +++ b/owncloudComLibrary/build.gradle @@ -7,6 +7,8 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" api 'com.gitlab.ownclouders:dav4android:oc_support_2.1.5' api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2' + debugImplementation 'com.facebook.stetho:stetho:1.5.1' + debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.5.1' // Moshi implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index a052924f..70ee7136 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -21,7 +21,7 @@ class ConnectionValidator ( var validationRetryCount = 0 val client = OwnCloudClient(baseClient.baseUri, null, false) if (clearCookiesOnValidation) { - client.clearCookies() + client.clearCookies(); } else { client.cookiesForBaseUri = baseClient.cookiesForBaseUri } @@ -84,7 +84,7 @@ class ConnectionValidator ( } private fun triggerAuthRefresh(): OwnCloudCredentials { - //TODO: Implement me + Timber.d("!!!!!!!!!!!!!!!!!!!!!!!!!!!! need to reauthenticate !!!!!!!!!!!!!!!!!!!!!!!!!!") return OwnCloudCredentialsFactory.getAnonymousCredentials() } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index eb3432b2..330a09fa 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -340,10 +340,6 @@ public class OwnCloudClient extends HttpClient { HttpUrl.parse(mBaseUri.toString())); } - public void clearCookies() { - setCookiesForBaseUri(new ArrayList<>()); - } - public OwnCloudVersion getOwnCloudVersion() { return mVersion; } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/CookieJarImpl.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/CookieJarImpl.kt index ae76424e..ec355cd7 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/CookieJarImpl.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/CookieJarImpl.kt @@ -28,7 +28,7 @@ import okhttp3.CookieJar import okhttp3.HttpUrl class CookieJarImpl( - private val sCookieStore: HashMap> + private val cookieStore: HashMap> ) : CookieJar { fun containsCookieWithName(cookies: List, name: String): Boolean { @@ -52,12 +52,11 @@ class CookieJarImpl( override fun saveFromResponse(url: HttpUrl, cookies: List) { // Avoid duplicated cookies but update - val currentCookies: List = sCookieStore[url.host] ?: ArrayList() + val currentCookies: List = cookieStore[url.host] ?: ArrayList() val updatedCookies: List = getUpdatedCookies(currentCookies, cookies) - sCookieStore[url.host] = updatedCookies + cookieStore[url.host] = updatedCookies } override fun loadForRequest(url: HttpUrl) = - sCookieStore[url.host] ?: ArrayList() - -} + cookieStore[url.host] ?: ArrayList() +} \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java index b231ffed..f1089f5e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java @@ -26,6 +26,7 @@ package com.owncloud.android.lib.common.http; import android.content.Context; +import com.facebook.stetho.okhttp3.StethoInterceptor; import com.owncloud.android.lib.common.network.AdvancedX509TrustManager; import com.owncloud.android.lib.common.network.NetworkUtils; import okhttp3.Cookie; @@ -139,4 +140,8 @@ public class HttpClient { public static void setContext(Context context) { sContext = context; } + + public void clearCookies() { + sCookieStore.clear(); + } } From e27a968ddb3b6d0e54a53d6b0a36935a4f31cc54 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 14 Sep 2021 14:36:49 +0200 Subject: [PATCH 14/23] add update authToken code to connectionValidator --- .../android/lib/common/ConnectionValidator.kt | 103 +++++++++++++++++- .../android/lib/common/OwnCloudClient.java | 19 ++-- .../lib/common/SingleSessionManager.java | 9 +- 3 files changed, 112 insertions(+), 19 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index 70ee7136..4a932058 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -1,31 +1,37 @@ 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 +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials import com.owncloud.android.lib.common.http.HttpConstants -import com.owncloud.android.lib.common.http.methods.HttpBaseMethod 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 class ConnectionValidator ( + val context: Context, val clearCookiesOnValidation: Boolean ){ - fun validate(baseClient: OwnCloudClient): Boolean { + fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager): Boolean { try { var validationRetryCount = 0 - val client = OwnCloudClient(baseClient.baseUri, null, false) + val client = OwnCloudClient(baseClient.baseUri, null, false, singleSessionManager) if (clearCookiesOnValidation) { client.clearCookies(); } else { client.cookiesForBaseUri = baseClient.cookiesForBaseUri } + client.account = baseClient.account client.credentials = baseClient.credentials while (validationRetryCount < 5) { Timber.d("+++++++++++++++++++++++++++++++++++++ validationRetryCout %d", validationRetryCount) @@ -40,7 +46,7 @@ class ConnectionValidator ( } // 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 OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials) { + if(baseClient.credentials !is OwnCloudAnonymousCredentials) { client.setFollowRedirects(false) val contentReply = canAccessRootFolder(client) if (contentReply.httpCode == HttpConstants.HTTP_OK) { @@ -51,8 +57,8 @@ class ConnectionValidator ( } } else { failCounter++ - if (contentReply.hashCode() == HttpConstants.HTTP_UNAUTHORIZED) { - triggerAuthRefresh() + if (contentReply.httpCode == HttpConstants.HTTP_UNAUTHORIZED) { + checkUnauthorizedAccess(client, singleSessionManager, contentReply.httpCode) } } } @@ -92,4 +98,89 @@ class ConnectionValidator ( 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 { + 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 + } } \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 330a09fa..a1bb2526 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -59,7 +59,6 @@ public class OwnCloudClient extends HttpClient { 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 = 5; - private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1; private static int sIntanceCounter = 0; private OwnCloudCredentials mCredentials = null; @@ -80,12 +79,13 @@ public class OwnCloudClient extends HttpClient { private boolean mFollowRedirects = false; - public OwnCloudClient(Uri baseUri, ConnectionValidator connectionValidator, boolean synchronizeRequests) { + public OwnCloudClient(Uri baseUri, ConnectionValidator connectionValidator, boolean synchronizeRequests, SingleSessionManager singleSessionManager) { if (baseUri == null) { throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL"); } mBaseUri = baseUri; mSynchronizeRequests = synchronizeRequests; + mSingleSessionManager = singleSessionManager; mInstanceNumber = sIntanceCounter++; Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient"); @@ -134,8 +134,10 @@ public class OwnCloudClient extends HttpClient { stacklog(status, method); if (mConnectionValidator != null && - status == HttpConstants.HTTP_MOVED_TEMPORARILY) { - retry = mConnectionValidator.validate(this); // retry on success fail on no success + (status == HttpConstants.HTTP_MOVED_TEMPORARILY || + (!(mCredentials instanceof OwnCloudAnonymousCredentials) && + status == HttpConstants.HTTP_UNAUTHORIZED))) { + retry = mConnectionValidator.validate(this, mSingleSessionManager); // retry on success fail on no success } else if (mFollowRedirects) { status = followRedirection(method).getLastStatus(); } @@ -145,8 +147,8 @@ public class OwnCloudClient extends HttpClient { if (repeatWithFreshCredentials) { repeatCounter++; } - */ + } while (retry); return status; @@ -163,6 +165,7 @@ public class OwnCloudClient extends HttpClient { "\nMethod: " + method.toString() + "\nUrl: " + method.getHttpUrl() + "\nCookeis: " + getCookiesForBaseUri().toString() + + "\nCredentials type: " + mCredentials.getClass().toString() + "\ntrace: " + ExceptionUtils.getStackTrace(e) + "---------------------------"); } @@ -336,8 +339,8 @@ public class OwnCloudClient extends HttpClient { } public List getCookiesForBaseUri() { - return getOkHttpClient().cookieJar().loadForRequest( - HttpUrl.parse(mBaseUri.toString())); + return getOkHttpClient().cookieJar().loadForRequest( + HttpUrl.parse(mBaseUri.toString())); } public OwnCloudVersion getOwnCloudVersion() { @@ -374,7 +377,7 @@ public class OwnCloudClient extends HttpClient { invalidateAccountCredentials(); if (getCredentials().authTokenCanBeRefreshed() && - repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) { + repeatCounter < 1) { try { mAccount.loadCredentials(getContext()); // if mAccount.getCredentials().length() == 0 --> refresh failed diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java index 225c48b0..e2c75c34 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/SingleSessionManager.java @@ -77,9 +77,8 @@ public class SingleSessionManager { sUserAgent = userAgent; } - private static OwnCloudClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects, ConnectionValidator connectionValidator) { - OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true); - client.setFollowRedirects(followRedirects); + private static OwnCloudClient createOwnCloudClient(Uri uri, Context context, ConnectionValidator connectionValidator, SingleSessionManager singleSessionManager) { + OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true, singleSessionManager); HttpClient.setContext(context); return client; @@ -132,8 +131,8 @@ public class SingleSessionManager { client = createOwnCloudClient( account.getBaseUri(), context.getApplicationContext(), - true, - connectionValidator); // TODO remove dependency on OwnCloudClientFactory + connectionValidator, + this); //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 From 5582097ca10050a04e1c3e9d314136615f13f5c8 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 14 Sep 2021 15:01:45 +0200 Subject: [PATCH 15/23] get access token to update through connection validator update token --- .../android/lib/common/ConnectionValidator.kt | 11 ++++------- .../android/lib/common/OwnCloudClient.java | 14 ++++++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index 4a932058..492a0096 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -4,7 +4,6 @@ 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 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 @@ -63,7 +62,8 @@ class ConnectionValidator ( } } if (successCounter >= failCounter) { - //update credentials in client + baseClient.credentials = client.credentials + baseClient.cookiesForBaseUri = client.cookiesForBaseUri return true } validationRetryCount++ @@ -89,11 +89,6 @@ class ConnectionValidator ( return remoteStatusOperation.execute(client) } - private fun triggerAuthRefresh(): OwnCloudCredentials { - Timber.d("!!!!!!!!!!!!!!!!!!!!!!!!!!!! need to reauthenticate !!!!!!!!!!!!!!!!!!!!!!!!!!") - return OwnCloudCredentialsFactory.getAnonymousCredentials() - } - private fun canAccessRootFolder(client: OwnCloudClient): RemoteOperationResult { val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation("/", true) return checkPathExistenceRemoteOperation.execute(client) @@ -154,8 +149,10 @@ class ConnectionValidator ( 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 diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index a1bb2526..04f268e7 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -59,6 +59,7 @@ public class OwnCloudClient extends HttpClient { 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 = 5; + private static final int MAX_RETRY_COUNT = 2; private static int sIntanceCounter = 0; private OwnCloudCredentials mCredentials = null; @@ -112,12 +113,12 @@ public class OwnCloudClient extends HttpClient { } private int saveExecuteHttpMethod(HttpBaseMethod method) throws Exception { - boolean repeatWithFreshCredentials; int repeatCounter = 0; int status; - boolean retry = false; + boolean retry; do { + repeatCounter++; retry = false; String requestId = RandomUtils.generateRandomUUID(); @@ -126,7 +127,7 @@ 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()); } @@ -149,7 +150,7 @@ public class OwnCloudClient extends HttpClient { } */ - } while (retry); + } while (retry && repeatCounter < MAX_RETRY_COUNT); return status; } @@ -166,6 +167,11 @@ public class OwnCloudClient extends HttpClient { "\nUrl: " + method.getHttpUrl() + "\nCookeis: " + getCookiesForBaseUri().toString() + "\nCredentials type: " + mCredentials.getClass().toString() + + "\ntoken: " + mCredentials.getAuthToken() + + + "\nHeaders: ++++" + + "\n" + method.getRequest().headers().toString() + + "+++++++++++++" + "\ntrace: " + ExceptionUtils.getStackTrace(e) + "---------------------------"); } From c59d1540c72e9c3fa82c291ba0aeb4e7f918bfbc Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 14 Sep 2021 16:54:07 +0200 Subject: [PATCH 16/23] clean up credentials stuff from owncloudclient --- .../android/lib/common/OwnCloudClient.java | 121 ++---------------- 1 file changed, 10 insertions(+), 111 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 04f268e7..fc58cfef 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -25,8 +25,6 @@ package com.owncloud.android.lib.common; -import android.accounts.AccountManager; -import android.accounts.AccountsException; import android.net.Uri; import at.bitfire.dav4jvm.exception.HttpException; @@ -47,7 +45,6 @@ import timber.log.Timber; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; import java.util.List; import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER; @@ -143,13 +140,6 @@ public class OwnCloudClient extends HttpClient { status = followRedirection(method).getLastStatus(); } - /* - repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter); - if (repeatWithFreshCredentials) { - repeatCounter++; - } - */ - } while (retry && repeatCounter < MAX_RETRY_COUNT); return status; @@ -178,29 +168,18 @@ public class OwnCloudClient extends HttpClient { } private int executeRedirectedHttpMethod(HttpBaseMethod method) throws Exception { - boolean repeatWithFreshCredentials; - int repeatCounter = 0; int status; + String requestId = RandomUtils.generateRandomUUID(); - 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); - + // 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(); return status; } @@ -365,86 +344,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. - *

- * Invalidates current credentials if the request failed as anauthorized. - *

- * 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)) { - invalidateAccountCredentials(); - - if (getCredentials().authTokenCanBeRefreshed() && - repeatCounter < 1) { - 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. - *

- * {@link #shouldInvalidateAccountCredentials(int)} should be called first. - * - */ - private void 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 - } - public boolean getFollowRedirects() { return mFollowRedirects; } From ce761aaec21222e2de4e9d0d0a17d3746ce77e94 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 14 Sep 2021 17:35:05 +0200 Subject: [PATCH 17/23] remove redirect code from owncloudclient use okhttp instead --- .../android/lib/common/OwnCloudClient.java | 87 +------------------ .../operations/RemoteOperationResult.java | 6 +- .../CheckPathExistenceRemoteOperation.kt | 14 --- 3 files changed, 6 insertions(+), 101 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index fc58cfef..65e2dada 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -128,16 +128,16 @@ public class OwnCloudClient extends HttpClient { method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); } + method.setFollowRedirects(mFollowRedirects); status = method.execute(); stacklog(status, method); - if (mConnectionValidator != null && + if (!mFollowRedirects && + mConnectionValidator != null && (status == HttpConstants.HTTP_MOVED_TEMPORARILY || (!(mCredentials instanceof OwnCloudAnonymousCredentials) && status == HttpConstants.HTTP_UNAUTHORIZED))) { retry = mConnectionValidator.validate(this, mSingleSessionManager); // retry on success fail on no success - } else if (mFollowRedirects) { - status = followRedirection(method).getLastStatus(); } } while (retry && repeatCounter < MAX_RETRY_COUNT); @@ -167,87 +167,6 @@ public class OwnCloudClient extends HttpClient { } } - private int executeRedirectedHttpMethod(HttpBaseMethod method) throws Exception { - int status; - 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(); - 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 hostClientManager - // due to it will be set a different url - exhaustResponse(method.getResponseBodyAsStream()); - - Timber.d("+++++++++++++++++++++++++++++++++++++++ %s", getFullUrl(location)); - method.setUrl(getFullUrl(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; - } - - private HttpUrl getFullUrl(String redirection) { - if(redirection.startsWith("/")) { - return HttpUrl.parse(mBaseUri.toString() + redirection); - } else { - return HttpUrl.parse(redirection); - } - } - /** * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation. * diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java index 4f707821..a30a43a5 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java @@ -66,7 +66,7 @@ public class RemoteOperationResult private String mHttpPhrase = null; private Exception mException = null; private ResultCode mCode = ResultCode.UNKNOWN_ERROR; - private String mRedirectedLocation; + private String mRedirectedLocation = ""; private List mAuthenticate = new ArrayList<>(); private String mLastPermanentLocation = null; private T mData = null; @@ -257,11 +257,11 @@ public class RemoteOperationResult this(httpCode, httpPhrase); if (headers != null) { for (Map.Entry> 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()); } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt index 5e0d974c..94fb7227 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt @@ -51,16 +51,8 @@ class CheckPathExistenceRemoteOperation( val remotePath: String? = "", val isUserLogged: Boolean ) : RemoteOperation() { - /** - * 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 { - val previousFollowRedirects = client.getFollowRedirects() return try { val stringUrl = if (isUserLogged) client.baseFilesWebDavUri.toString() @@ -72,10 +64,6 @@ class CheckPathExistenceRemoteOperation( } 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. @@ -93,8 +81,6 @@ class CheckPathExistenceRemoteOperation( "Existence check for ${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)} : ${result.logMessage}" ) result - } finally { - client.setFollowRedirects(previousFollowRedirects) } } From 5ca99a0e69ffeca698b172ba5bd9253ea164ae10 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Thu, 16 Sep 2021 14:20:57 +0200 Subject: [PATCH 18/23] update base url in active client after 301 redirect reduce validation retry count --- .../android/lib/common/ConnectionValidator.kt | 6 +++++- .../android/lib/common/OwnCloudClient.java | 15 +++++++++++++-- .../lib/common/http/methods/HttpBaseMethod.kt | 15 +++++++++++++++ .../status/GetRemoteStatusOperation.kt | 18 +++++++----------- .../lib/resources/status/StatusRequester.kt | 4 +++- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index 492a0096..3341531e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -32,7 +32,7 @@ class ConnectionValidator ( client.account = baseClient.account client.credentials = baseClient.credentials - while (validationRetryCount < 5) { + while (validationRetryCount < VALIDATION_RETRY_COUNT) { Timber.d("+++++++++++++++++++++++++++++++++++++ validationRetryCout %d", validationRetryCount) var successCounter = 0 var failCounter = 0 @@ -180,4 +180,8 @@ class ConnectionValidator ( } return credentialsWereRefreshed } + + companion object { + val VALIDATION_RETRY_COUNT = 3 + } } \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 65e2dada..54dddbae 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -48,6 +48,7 @@ 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.HTTP_MOVED_PERMANENTLY; import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID; public class OwnCloudClient extends HttpClient { @@ -77,7 +78,10 @@ public class OwnCloudClient extends HttpClient { private boolean mFollowRedirects = false; - public OwnCloudClient(Uri baseUri, ConnectionValidator connectionValidator, boolean synchronizeRequests, SingleSessionManager singleSessionManager) { + public OwnCloudClient(Uri baseUri, + ConnectionValidator connectionValidator, + boolean synchronizeRequests, + SingleSessionManager singleSessionManager) { if (baseUri == null) { throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL"); } @@ -113,6 +117,10 @@ public class OwnCloudClient extends HttpClient { int repeatCounter = 0; int status; + if(mFollowRedirects) { + method.setFollowRedirects(true); + } + boolean retry; do { repeatCounter++; @@ -128,16 +136,19 @@ public class OwnCloudClient extends HttpClient { method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); } - method.setFollowRedirects(mFollowRedirects); status = method.execute(); stacklog(status, method); if (!mFollowRedirects && + !method.getFollowRedirects() && mConnectionValidator != null && (status == HttpConstants.HTTP_MOVED_TEMPORARILY || (!(mCredentials instanceof OwnCloudAnonymousCredentials) && status == HttpConstants.HTTP_UNAUTHORIZED))) { retry = mConnectionValidator.validate(this, mSingleSessionManager); // retry on success fail on no success + } else if(method.getFollowPermanentRedirects() && status == HTTP_MOVED_PERMANENTLY) { + retry = true; + method.setFollowRedirects(true); } } while (retry && repeatCounter < MAX_RETRY_COUNT); diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/HttpBaseMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/HttpBaseMethod.kt index 5b1f1ab5..bc8fe1ce 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/HttpBaseMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/HttpBaseMethod.kt @@ -41,6 +41,7 @@ abstract class HttpBaseMethod constructor(url: URL) { var okHttpClient: OkHttpClient var httpUrl: HttpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException() var request: Request + private var _followPermanentRedirects = false abstract var response: Response var call: Call? = null @@ -123,6 +124,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 *** *************************/ @@ -158,6 +164,15 @@ abstract class HttpBaseMethod constructor(url: URL) { .build() } + open fun getFollowRedirects() = okHttpClient.followRedirects + + open fun setFollPermanentRedirects(followRedirects: Boolean) { + _followPermanentRedirects = followRedirects + } + + open fun getFollowPermanentRedirects() = _followPermanentRedirects + + /************ *** Call *** ************/ diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt index 21f02da0..38f8af82 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt @@ -48,17 +48,11 @@ class GetRemoteStatusOperation : RemoteOperation() { if(!usesHttpOrHttps(client.baseUri)) { client.baseUri = buildFullHttpsUrl(client.baseUri) } + return tryToConnect(client) + } - 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 result + private fun updateClientBaseUrl(client:OwnCloudClient, newBaseUrl:String) { + client.baseUri = Uri.parse(newBaseUrl) } private fun tryToConnect(client: OwnCloudClient): RemoteOperationResult { @@ -66,7 +60,9 @@ class GetRemoteStatusOperation : RemoteOperation() { return try { val requester = StatusRequester() val requestResult = requester.request(baseUrl, client) - requester.handleRequestResult(requestResult, baseUrl) + val result = requester.handleRequestResult(requestResult, baseUrl) + updateClientBaseUrl(client, result.data.baseUrl) + return result } catch (e: JSONException) { RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED) } catch (e: Exception) { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt index e1fdb441..b201a937 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt @@ -83,8 +83,10 @@ internal class StatusRequester { var status: Int val getMethod = getGetMethod(currentLocation) + getMethod.setFollPermanentRedirects(true) status = client.executeHttpMethod(getMethod) - return RequestResult(getMethod, status, redirectedToUnsecureLocation, currentLocation) + + return RequestResult(getMethod, status, redirectedToUnsecureLocation, getMethod.getFinalUrl().toString()) } private fun Int.isSuccess() = this == HttpConstants.HTTP_OK From fc8440cc0178b2afe4c3be4c0b2532beb1ecc781 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Sun, 21 Nov 2021 12:48:47 +0100 Subject: [PATCH 19/23] remove debug statements --- .../android/lib/common/ConnectionValidator.kt | 2 +- .../android/lib/common/OwnCloudClient.java | 24 ------------------- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index 3341531e..7a8789db 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -33,7 +33,7 @@ class ConnectionValidator ( client.account = baseClient.account client.credentials = baseClient.credentials while (validationRetryCount < VALIDATION_RETRY_COUNT) { - Timber.d("+++++++++++++++++++++++++++++++++++++ validationRetryCout %d", validationRetryCount) + Timber.d("validationRetryCout %d", validationRetryCount) var successCounter = 0 var failCounter = 0 diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 54dddbae..7aae9c28 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -56,7 +56,6 @@ public class OwnCloudClient extends HttpClient { public static final String WEBDAV_FILES_PATH_4_0 = "/remote.php/dav/files/"; 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 = 5; private static final int MAX_RETRY_COUNT = 2; private static int sIntanceCounter = 0; @@ -137,7 +136,6 @@ public class OwnCloudClient extends HttpClient { } status = method.execute(); - stacklog(status, method); if (!mFollowRedirects && !method.getFollowRedirects() && @@ -156,28 +154,6 @@ public class OwnCloudClient extends HttpClient { return status; } - private void stacklog(int status, HttpBaseMethod method) { - try { - throw new Exception("Stack log"); - } catch(Exception e) { - Timber.d("\n---------------------------" + - "\nresponsecode: " + status + - "\nThread: " + Thread.currentThread().getName() + - "\nobject: " + this.toString() + - "\nMethod: " + method.toString() + - "\nUrl: " + method.getHttpUrl() + - "\nCookeis: " + getCookiesForBaseUri().toString() + - "\nCredentials type: " + mCredentials.getClass().toString() + - "\ntoken: " + mCredentials.getAuthToken() + - - "\nHeaders: ++++" + - "\n" + method.getRequest().headers().toString() + - "+++++++++++++" + - "\ntrace: " + ExceptionUtils.getStackTrace(e) + - "---------------------------"); - } - } - /** * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation. * From 4f3e167efac41ce75dbbb20a9a49fcf71126bb97 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Thu, 24 Feb 2022 13:44:41 +0100 Subject: [PATCH 20/23] fix spelling mistakes --- .../resources/files/CheckPathExistenceRemoteOperation.kt | 6 +++--- .../files/services/implementation/OCFileService.kt | 2 +- .../status/services/implementation/OCServerInfoService.kt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt index 94fb7227..47c1827e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt @@ -44,18 +44,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() { override fun run(client: OwnCloudClient): RemoteOperationResult { 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 { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index ce9dc78b..522a5ac4 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -33,6 +33,6 @@ class OCFileService(override val client: OwnCloudClient) : override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult = CheckPathExistenceRemoteOperation( remotePath = path, - isUserLogged = isUserLogged + isUserLoggedIn = isUserLogged ).execute(client) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCServerInfoService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCServerInfoService.kt index ea455770..a9809feb 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCServerInfoService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCServerInfoService.kt @@ -42,7 +42,7 @@ class OCServerInfoService : ServerInfoService { ): RemoteOperationResult = CheckPathExistenceRemoteOperation( remotePath = path, - isUserLogged = true + isUserLoggedIn = true ).execute(client) override fun getRemoteStatus( From d9dce81ce70620fb71afcf1e8ed3ef86df15a067 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Thu, 24 Feb 2022 13:42:36 +0100 Subject: [PATCH 21/23] add GetBaseUrlRemoteOperation --- .../CheckPathExistenceRemoteOperation.kt | 1 - .../files/GetBaseUrlRemoteOperation.kt | 73 +++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt index 47c1827e..e59388bd 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CheckPathExistenceRemoteOperation.kt @@ -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 diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt new file mode 100644 index 00000000..52345792 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt @@ -0,0 +1,73 @@ +/* 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.network.WebdavUtils +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() { + override fun run(client: OwnCloudClient): RemoteOperationResult { + return try { + val stringUrl = client.baseFilesWebDavUri.toString() + + val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropset).apply { + setReadTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) + setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) + } + + val status = client.executeHttpMethod(propFindMethod) + + if (isSuccess(status)) RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { + data = propFindMethod.getFinalUrl().toString() + } + else RemoteOperationResult(propFindMethod).apply { data = null } + } catch (e: Exception) { + Timber.e(e, "Could not get actuall (or redirected) base URL from base url (/).") + RemoteOperationResult(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 = 10000 + } +} \ No newline at end of file From 2a4195c966f95c8459ef23522896141d83a8f37e Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Fri, 25 Feb 2022 12:50:58 +0100 Subject: [PATCH 22/23] fix lint and compile issues --- .../android/lib/common/ConnectionValidator.kt | 10 +- .../android/lib/common/http/HttpClient.java | 1 - .../http/methods/webdav/PropfindMethod.kt | 2 +- .../files/GetBaseUrlRemoteOperation.kt | 5 +- .../status/GetRemoteStatusOperation.kt | 6 +- .../lib/sampleclient/MainActivity.java | 302 ++++++++++++++++++ 6 files changed, 312 insertions(+), 14 deletions(-) create mode 100644 sample_client/src/main/java/com/owncloud/android/lib/sampleclient/MainActivity.java diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index 7a8789db..a793b4fd 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -15,17 +15,17 @@ import timber.log.Timber import java.io.IOException import java.lang.Exception -class ConnectionValidator ( +class ConnectionValidator( val context: Context, val clearCookiesOnValidation: Boolean -){ +) { fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager): Boolean { try { var validationRetryCount = 0 val client = OwnCloudClient(baseClient.baseUri, null, false, singleSessionManager) if (clearCookiesOnValidation) { - client.clearCookies(); + client.clearCookies() } else { client.cookiesForBaseUri = baseClient.cookiesForBaseUri } @@ -45,7 +45,7 @@ class ConnectionValidator ( } // 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) { + if (baseClient.credentials !is OwnCloudAnonymousCredentials) { client.setFollowRedirects(false) val contentReply = canAccessRootFolder(client) if (contentReply.httpCode == HttpConstants.HTTP_OK) { @@ -184,4 +184,4 @@ class ConnectionValidator ( companion object { val VALIDATION_RETRY_COUNT = 3 } -} \ No newline at end of file +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java index f1089f5e..51d58b53 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java @@ -26,7 +26,6 @@ package com.owncloud.android.lib.common.http; import android.content.Context; -import com.facebook.stetho.okhttp3.StethoInterceptor; import com.owncloud.android.lib.common.network.AdvancedX509TrustManager; import com.owncloud.android.lib.common.network.NetworkUtils; import okhttp3.Cookie; diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PropfindMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PropfindMethod.kt index 66cc313b..fb0e5a15 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PropfindMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PropfindMethod.kt @@ -50,7 +50,7 @@ class PropfindMethod( public override fun onExecute(): Int { davResource.propfind( depth = depth, - reqProp = *propertiesToRequest, + reqProp = propertiesToRequest, listOfHeaders = super.getRequestHeadersAsHashMap(), callback = { response: Response, hrefRelation: HrefRelation? -> when (hrefRelation) { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt index 52345792..b0b3865c 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt @@ -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 import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod -import com.owncloud.android.lib.common.network.WebdavUtils import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import timber.log.Timber @@ -58,7 +57,7 @@ class GetBaseUrlRemoteOperation : RemoteOperation() { else RemoteOperationResult(propFindMethod).apply { data = null } } catch (e: Exception) { Timber.e(e, "Could not get actuall (or redirected) base URL from base url (/).") - RemoteOperationResult(e); + RemoteOperationResult(e) } } @@ -70,4 +69,4 @@ class GetBaseUrlRemoteOperation : RemoteOperation() { */ private const val TIMEOUT = 10000 } -} \ No newline at end of file +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt index 38f8af82..12be4dab 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.kt @@ -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,13 +43,13 @@ import timber.log.Timber class GetRemoteStatusOperation : RemoteOperation() { public override fun run(client: OwnCloudClient): RemoteOperationResult { - if(!usesHttpOrHttps(client.baseUri)) { + if (!usesHttpOrHttps(client.baseUri)) { client.baseUri = buildFullHttpsUrl(client.baseUri) } return tryToConnect(client) } - private fun updateClientBaseUrl(client:OwnCloudClient, newBaseUrl:String) { + private fun updateClientBaseUrl(client: OwnCloudClient, newBaseUrl: String) { client.baseUri = Uri.parse(newBaseUrl) } diff --git a/sample_client/src/main/java/com/owncloud/android/lib/sampleclient/MainActivity.java b/sample_client/src/main/java/com/owncloud/android/lib/sampleclient/MainActivity.java new file mode 100644 index 00000000..5da47632 --- /dev/null +++ b/sample_client/src/main/java/com/owncloud/android/lib/sampleclient/MainActivity.java @@ -0,0 +1,302 @@ +/* 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.ConnectionValidator; +import com.owncloud.android.lib.common.OwnCloudClient; +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 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(); + mHandler = new Handler(); + + final Uri serverUri = Uri.parse(getString(R.string.server_base_url)); + + SingleSessionManager.setUserAgent(getUserAgent()); + SingleSessionManager.setConnectionValidator(new ConnectionValidator(this, false)); + mClient = new OwnCloudClient(serverUri, + SingleSessionManager.getConnectionValidator(), + true, + SingleSessionManager.getDefaultSingleton()); + 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 files = new ArrayList<>(); + for (RemoteFile remoteFile : (List) 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); + } +} \ No newline at end of file From 7c77c267f9e2258d4f5be4d5f2f93b83ec31b6ee Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Wed, 2 Mar 2022 16:09:04 +0100 Subject: [PATCH 23/23] apply changes according to review --- owncloudComLibrary/build.gradle | 2 -- .../android/lib/common/ConnectionValidator.kt | 3 +-- .../android/lib/common/OwnCloudClient.java | 24 ++++++++----------- .../lib/common/http/methods/HttpBaseMethod.kt | 2 +- .../operations/RemoteOperationResult.java | 6 +++-- .../files/GetBaseUrlRemoteOperation.kt | 17 ++++++++----- .../lib/resources/status/StatusRequester.kt | 16 ++++--------- 7 files changed, 32 insertions(+), 38 deletions(-) diff --git a/owncloudComLibrary/build.gradle b/owncloudComLibrary/build.gradle index c98d293f..e9f20869 100644 --- a/owncloudComLibrary/build.gradle +++ b/owncloudComLibrary/build.gradle @@ -7,8 +7,6 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" api 'com.gitlab.ownclouders:dav4android:oc_support_2.1.5' api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2' - debugImplementation 'com.facebook.stetho:stetho:1.5.1' - debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.5.1' // Moshi implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt index a793b4fd..d5f2aabd 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -19,7 +19,6 @@ class ConnectionValidator( val context: Context, val clearCookiesOnValidation: Boolean ) { - fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager): Boolean { try { var validationRetryCount = 0 @@ -182,6 +181,6 @@ class ConnectionValidator( } companion object { - val VALIDATION_RETRY_COUNT = 3 + private val VALIDATION_RETRY_COUNT = 3 } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index 7aae9c28..09b255e2 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -27,7 +27,6 @@ package com.owncloud.android.lib.common; 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; @@ -35,12 +34,10 @@ 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; import okhttp3.HttpUrl; -import org.apache.commons.lang3.exception.ExceptionUtils; import timber.log.Timber; import java.io.IOException; @@ -49,7 +46,6 @@ import java.util.List; import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER; import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY; -import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID; public class OwnCloudClient extends HttpClient { @@ -137,12 +133,7 @@ public class OwnCloudClient extends HttpClient { status = method.execute(); - if (!mFollowRedirects && - !method.getFollowRedirects() && - mConnectionValidator != null && - (status == HttpConstants.HTTP_MOVED_TEMPORARILY || - (!(mCredentials instanceof OwnCloudAnonymousCredentials) && - status == HttpConstants.HTTP_UNAUTHORIZED))) { + if (shouldConnectionValidatorBeCalled(method, status)) { retry = mConnectionValidator.validate(this, mSingleSessionManager); // retry on success fail on no success } else if(method.getFollowPermanentRedirects() && status == HTTP_MOVED_PERMANENTLY) { retry = true; @@ -154,6 +145,15 @@ public class OwnCloudClient extends HttpClient { return status; } + private boolean shouldConnectionValidatorBeCalled(HttpBaseMethod method, int status) { + return !mFollowRedirects && + !method.getFollowRedirects() && + mConnectionValidator != null && + (status == HttpConstants.HTTP_MOVED_TEMPORARILY || + (!(mCredentials instanceof OwnCloudAnonymousCredentials) && + status == HttpConstants.HTTP_UNAUTHORIZED)); + } + /** * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation. * @@ -250,10 +250,6 @@ public class OwnCloudClient extends HttpClient { this.mAccount = account; } - public boolean getFollowRedirects() { - return mFollowRedirects; - } - public void setFollowRedirects(boolean followRedirects) { this.mFollowRedirects = followRedirects; } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/HttpBaseMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/HttpBaseMethod.kt index bc8fe1ce..ee1d2c0e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/HttpBaseMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/HttpBaseMethod.kt @@ -166,7 +166,7 @@ abstract class HttpBaseMethod constructor(url: URL) { open fun getFollowRedirects() = okHttpClient.followRedirects - open fun setFollPermanentRedirects(followRedirects: Boolean) { + open fun setFollowPermanentRedirects(followRedirects: Boolean) { _followPermanentRedirects = followRedirects } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java index a30a43a5..7430771d 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java @@ -60,6 +60,8 @@ public class RemoteOperationResult * 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; @@ -257,11 +259,11 @@ public class RemoteOperationResult this(httpCode, httpPhrase); if (headers != null) { for (Map.Entry> header : headers.toMultimap().entrySet()) { - if ("location".equalsIgnoreCase(header.getKey())) { + if (LOCATION.equalsIgnoreCase(header.getKey())) { mRedirectedLocation = header.getValue().get(0); continue; } - if ("www-authenticate".equalsIgnoreCase(header.getKey())) { + if (WWW_AUTHENTICATE.equalsIgnoreCase(header.getKey())) { for (String value: header.getValue()) { mAuthenticate.add(value.toLowerCase()); } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt index b0b3865c..904a9bea 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt @@ -45,16 +45,21 @@ class GetBaseUrlRemoteOperation : RemoteOperation() { val stringUrl = client.baseFilesWebDavUri.toString() val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropset).apply { - setReadTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) - setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) + setReadTimeout(TIMEOUT, TimeUnit.SECONDS) + setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS) } val status = client.executeHttpMethod(propFindMethod) - if (isSuccess(status)) RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { - data = propFindMethod.getFinalUrl().toString() + if (isSuccess(status)) { + RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { + data = propFindMethod.getFinalUrl().toString() + } + } else { + RemoteOperationResult(propFindMethod).apply { + data = null + } } - else RemoteOperationResult(propFindMethod).apply { data = null } } catch (e: Exception) { Timber.e(e, "Could not get actuall (or redirected) base URL from base url (/).") RemoteOperationResult(e) @@ -67,6 +72,6 @@ class GetBaseUrlRemoteOperation : RemoteOperation() { /** * Maximum time to wait for a response from the server in milliseconds. */ - private const val TIMEOUT = 10000 + private const val TIMEOUT = 10_000L } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt index b201a937..55381f90 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/StatusRequester.kt @@ -73,20 +73,18 @@ internal class StatusRequester { data class RequestResult( val getMethod: GetMethod, val status: Int, - val redirectedToUnsecureLocation: Boolean, val lastLocation: String ) fun request(baseLocation: String, client: OwnCloudClient): RequestResult { - var currentLocation = baseLocation + OwnCloudClient.STATUS_PATH - var redirectedToUnsecureLocation = false + val currentLocation = baseLocation + OwnCloudClient.STATUS_PATH var status: Int val getMethod = getGetMethod(currentLocation) - getMethod.setFollPermanentRedirects(true) + getMethod.setFollowPermanentRedirects(true) status = client.executeHttpMethod(getMethod) - return RequestResult(getMethod, status, redirectedToUnsecureLocation, getMethod.getFinalUrl().toString()) + return RequestResult(getMethod, status, getMethod.getFinalUrl().toString()) } private fun Int.isSuccess() = this == HttpConstants.HTTP_OK @@ -106,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 = - 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) - } + 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,