diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index f365456a..6ed56413 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -6,5 +6,5 @@ jobs: name: "Validation" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: gradle/wrapper-validation-action@v1 diff --git a/build.gradle b/build.gradle index 99ac23a2..aa8824db 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - kotlinVersion = '1.6.10' + kotlinVersion = '1.6.21' moshiVersion = "1.13.0" } @@ -10,8 +10,8 @@ buildscript { maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.1" - classpath 'com.android.tools.build:gradle:7.0.4' + classpath "org.jlleitschuh.gradle:ktlint-gradle:10.3.0" + classpath 'com.android.tools.build:gradle:7.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" } } diff --git a/check_code_script.sh b/check_code_script.sh new file mode 100755 index 00000000..98666e8e --- /dev/null +++ b/check_code_script.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +check_license_in_file() { + if ! head -n 20 $FILE | grep -q "Permission is hereby granted, free of charge, to any person obtaining a copy" + then + echo "$FILE does not contain a current copyright header" + fi +} + +for FILE in $(find owncloudComLibrary/src -name "*.java" -o -name "*.kt") +do + check_license_in_file +done + +./gradlew ktlintFormat diff --git a/owncloudComLibrary/build.gradle b/owncloudComLibrary/build.gradle index 98f4a691..cfcd453d 100644 --- a/owncloudComLibrary/build.gradle +++ b/owncloudComLibrary/build.gradle @@ -12,33 +12,27 @@ dependencies { implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") { exclude module: "kotlin-reflect" } + implementation 'org.apache.commons:commons-lang3:3.12.0' kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" testImplementation 'junit:junit:4.13.2' - testImplementation 'org.robolectric:robolectric:4.7.3' + testImplementation 'org.robolectric:robolectric:4.8.1' + debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.6.0' } android { - compileSdkVersion 30 + compileSdkVersion 31 defaultConfig { minSdkVersion 21 - targetSdkVersion 30 - - versionCode = 10001400 - versionName = "1.0.14" + targetSdkVersion 31 } - lintOptions { + lint { abortOnError false ignoreWarnings true } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - testOptions { unitTests { includeAndroidResources = true diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java b/owncloudComLibrary/src/debug/java/com/owncloud/android/lib/common/http/DebugInterceptorFactory.kt similarity index 50% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java rename to owncloudComLibrary/src/debug/java/com/owncloud/android/lib/common/http/DebugInterceptorFactory.kt index 948e8a07..b92c9b5a 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClientFactory.java +++ b/owncloudComLibrary/src/debug/java/com/owncloud/android/lib/common/http/DebugInterceptorFactory.kt @@ -19,40 +19,12 @@ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - * */ -package com.owncloud.android.lib.common; +package com.owncloud.android.lib.common.http -import android.content.Context; -import android.net.Uri; +import com.facebook.stetho.okhttp3.StethoInterceptor -import com.owncloud.android.lib.common.http.HttpClient; -import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation; - -public class OwnCloudClientFactory { - - /** - * Creates a OwnCloudClient to access a URL and sets the desired parameters for ownCloud - * client connections. - * - * @param uri URL to the ownCloud server; BASE ENTRY POINT, not WebDavPATH - * @param context Android context where the OwnCloudClient is being created. - * @return A OwnCloudClient object ready to be used - */ - public static OwnCloudClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) { - OwnCloudClient client = new OwnCloudClient(uri); - - client.setFollowRedirects(followRedirects); - - HttpClient.setContext(context); - retrieveCookiesFromMiddleware(client); - - return client; - } - - private static void retrieveCookiesFromMiddleware(OwnCloudClient client) { - final GetRemoteStatusOperation statusOperation = new GetRemoteStatusOperation(); - statusOperation.run(client); - } +object DebugInterceptorFactory { + fun getInterceptor() = StethoInterceptor() } 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..23a5a92f --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/ConnectionValidator.kt @@ -0,0 +1,215 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2016 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.common + +import android.accounts.AccountManager +import android.accounts.AccountsException +import android.content.Context +import com.owncloud.android.lib.common.authentication.OwnCloudCredentials +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation +import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation +import com.owncloud.android.lib.resources.status.RemoteServerInfo +import org.apache.commons.lang3.exception.ExceptionUtils +import timber.log.Timber +import java.io.IOException +import java.lang.Exception + +/** + * ConnectionValidator + * + * @author Christian Schabesberger + */ +class ConnectionValidator( + val context: Context, + val clearCookiesOnValidation: Boolean +) { + fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager, context: Context): Boolean { + try { + var validationRetryCount = 0 + val client = OwnCloudClient(baseClient.baseUri, null, false, singleSessionManager, context) + if (clearCookiesOnValidation) { + client.clearCookies() + } else { + client.cookiesForBaseUri = baseClient.cookiesForBaseUri + } + + client.account = baseClient.account + client.credentials = baseClient.credentials + while (validationRetryCount < VALIDATION_RETRY_COUNT) { + Timber.d("validationRetryCout %d", validationRetryCount) + var successCounter = 0 + var failCounter = 0 + + client.setFollowRedirects(true) + if (isOnwCloudStatusOk(client)) { + successCounter++ + } else { + failCounter++ + } + + // Skip the part where we try to check if we can access the parts where we have to be logged in... if we are not logged in + if (baseClient.credentials !is OwnCloudAnonymousCredentials) { + client.setFollowRedirects(false) + val contentReply = canAccessRootFolder(client) + if (contentReply.httpCode == HttpConstants.HTTP_OK) { + if (contentReply.data == true) { //if data is true it means that the content reply was ok + successCounter++ + } else { + failCounter++ + } + } else { + failCounter++ + if (contentReply.httpCode == HttpConstants.HTTP_UNAUTHORIZED) { + checkUnauthorizedAccess(client, singleSessionManager, contentReply.httpCode) + } + } + } + if (successCounter >= failCounter) { + baseClient.credentials = client.credentials + baseClient.cookiesForBaseUri = client.cookiesForBaseUri + return true + } + validationRetryCount++ + } + Timber.d("Could not authenticate or get valid data from owncloud") + } catch (e: Exception) { + Timber.d(ExceptionUtils.getStackTrace(e)) + } + return false + } + + private fun isOnwCloudStatusOk(client: OwnCloudClient): Boolean { + val reply = getOwnCloudStatus(client) + // dont check status code. It currently relais on the broken redirect code of the owncloud client + // TODO: Use okhttp redirect and add this check again + // return reply.httpCode == HttpConstants.HTTP_OK && + return !reply.isException && + reply.data != null + } + + private fun getOwnCloudStatus(client: OwnCloudClient): RemoteOperationResult { + val remoteStatusOperation = GetRemoteStatusOperation() + return remoteStatusOperation.execute(client) + } + + private fun canAccessRootFolder(client: OwnCloudClient): RemoteOperationResult { + val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation("/", true) + return checkPathExistenceRemoteOperation.execute(client) + } + + /** + * Determines if credentials should be invalidated according the to the HTTPS status + * of a network request just performed. + * + * @param httpStatusCode Result of the last request ran with the 'credentials' belows. + * @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or + * cannot be invalidated with the given arguments. + */ + private fun shouldInvalidateAccountCredentials(credentials: OwnCloudCredentials, account: OwnCloudAccount, httpStatusCode: Int): Boolean { + var shouldInvalidateAccountCredentials = httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED + shouldInvalidateAccountCredentials = shouldInvalidateAccountCredentials and // real credentials + (credentials !is OwnCloudAnonymousCredentials) + + // test if have all the needed to effectively invalidate ... + shouldInvalidateAccountCredentials = + shouldInvalidateAccountCredentials and (account.savedAccount != null) + return shouldInvalidateAccountCredentials + } + + /** + * Invalidates credentials stored for the given account in the system [AccountManager] and in + * current [SingleSessionManager.getDefaultSingleton] instance. + * + * + * [.shouldInvalidateAccountCredentials] should be called first. + * + */ + private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) { + val am = AccountManager.get(context) + am.invalidateAuthToken( + account.savedAccount.type, + credentials.authToken + ) + am.clearPassword(account.savedAccount) // being strict, only needed for Basic Auth credentials + } + + /** + * Checks the status code of an execution and decides if should be repeated with fresh credentials. + * + * + * Invalidates current credentials if the request failed as anauthorized. + * + * + * Refresh current credentials if possible, and marks a retry. + * + * @param status + * @param repeatCounter + * @return + */ + private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean { + var credentialsWereRefreshed = false + val account = client.account + val credentials = account.credentials + if (shouldInvalidateAccountCredentials(credentials, account, status)) { + invalidateAccountCredentials(account, credentials) + + if (credentials.authTokenCanBeRefreshed()) { + try { + // This command does the actual refresh + account.loadCredentials(context) + // if mAccount.getCredentials().length() == 0 --> refresh failed + client.credentials = account.credentials + credentialsWereRefreshed = true + } catch (e: AccountsException) { + Timber.e( + e, "Error while trying to refresh auth token for %s\ntrace: %s", + account.savedAccount.name, + ExceptionUtils.getStackTrace(e) + ) + } catch (e: IOException) { + Timber.e( + e, "Error while trying to refresh auth token for %s\ntrace: %s", + account.savedAccount.name, + ExceptionUtils.getStackTrace(e) + ) + } + if (!credentialsWereRefreshed) { + // if credentials are not refreshed, client must be removed + // from the OwnCloudClientManager to prevent it is reused once and again + singleSessionManager.removeClientFor(account) + } + } + // else: onExecute will finish with status 401 + } + return credentialsWereRefreshed + } + + companion object { + private val VALIDATION_RETRY_COUNT = 3 + } +} 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..e332b8fa 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,11 +25,9 @@ package com.owncloud.android.lib.common; -import android.accounts.AccountManager; -import android.accounts.AccountsException; +import android.content.Context; import android.net.Uri; -import at.bitfire.dav4jvm.exception.HttpException; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; @@ -37,7 +35,6 @@ import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory import com.owncloud.android.lib.common.http.HttpClient; import com.owncloud.android.lib.common.http.HttpConstants; import com.owncloud.android.lib.common.http.methods.HttpBaseMethod; -import com.owncloud.android.lib.common.network.RedirectionPath; import com.owncloud.android.lib.common.utils.RandomUtils; import com.owncloud.android.lib.resources.status.OwnCloudVersion; import okhttp3.Cookie; @@ -49,40 +46,54 @@ import java.io.InputStream; import java.util.List; import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER; -import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID; +import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY; public class OwnCloudClient extends HttpClient { public static final String WEBDAV_FILES_PATH_4_0 = "/remote.php/dav/files/"; - public static final String WEBDAV_PATH_4_0_AND_LATER = "/remote.php/dav"; public static final String STATUS_PATH = "/status.php"; private static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/"; - private static final int MAX_REDIRECTIONS_COUNT = 3; - private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1; + private static final int MAX_RETRY_COUNT = 2; - private static byte[] sExhaustBuffer = new byte[1024]; private static int sIntanceCounter = 0; private OwnCloudCredentials mCredentials = null; private int mInstanceNumber; private Uri mBaseUri; private OwnCloudVersion mVersion = null; private OwnCloudAccount mAccount; + private final ConnectionValidator mConnectionValidator; + private Object mRequestMutex = new Object(); + + // If set to true a mutex will be used to prevent parallel execution of the execute() method + // if false the execute() method can be called even though the mutex is already aquired. + // This is used for the ConnectionValidator, which has to be able to execute OperationsWhile all "normal" operations net + // to be set on hold. + private final Boolean mSynchronizeRequests; private SingleSessionManager mSingleSessionManager = null; - private boolean mFollowRedirects; + private boolean mFollowRedirects = false; + + public OwnCloudClient(Uri baseUri, + ConnectionValidator connectionValidator, + boolean synchronizeRequests, + SingleSessionManager singleSessionManager, + Context context) { + super(context); - public OwnCloudClient(Uri baseUri) { if (baseUri == null) { throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL"); } mBaseUri = baseUri; + mSynchronizeRequests = synchronizeRequests; + mSingleSessionManager = singleSessionManager; mInstanceNumber = sIntanceCounter++; Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient"); clearCredentials(); clearCookies(); + mConnectionValidator = connectionValidator; } public void clearCredentials() { @@ -92,11 +103,27 @@ public class OwnCloudClient extends HttpClient { } public int executeHttpMethod(HttpBaseMethod method) throws Exception { - boolean repeatWithFreshCredentials; + if (mSynchronizeRequests) { + synchronized (mRequestMutex) { + return saveExecuteHttpMethod(method); + } + } else { + return saveExecuteHttpMethod(method); + } + } + + private int saveExecuteHttpMethod(HttpBaseMethod method) throws Exception { int repeatCounter = 0; int status; + if (mFollowRedirects) { + method.setFollowRedirects(true); + } + + boolean retry; do { + repeatCounter++; + retry = false; String requestId = RandomUtils.generateRandomUUID(); // Header to allow tracing requests in apache and ownCloud logs @@ -104,105 +131,34 @@ public class OwnCloudClient extends HttpClient { method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId); method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent()); method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY); - if (mCredentials.getHeaderAuth() != null && method.getRequestHeader(AUTHORIZATION_HEADER) == null) { + if (mCredentials.getHeaderAuth() != null && !mCredentials.getHeaderAuth().isEmpty()) { method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); } - status = method.execute(); - if (mFollowRedirects) { - status = followRedirection(method).getLastStatus(); + status = method.execute(this); + + if (shouldConnectionValidatorBeCalled(method, status)) { + retry = mConnectionValidator.validate(this, mSingleSessionManager, getContext()); // retry on success fail on no success + } else if (method.getFollowPermanentRedirects() && status == HTTP_MOVED_PERMANENTLY) { + retry = true; + method.setFollowRedirects(true); } - repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter); - if (repeatWithFreshCredentials) { - repeatCounter++; - } - } while (repeatWithFreshCredentials); + } while (retry && repeatCounter < MAX_RETRY_COUNT); return status; } - private int executeRedirectedHttpMethod(HttpBaseMethod method) throws Exception { - boolean repeatWithFreshCredentials; - int repeatCounter = 0; - int status; + private boolean shouldConnectionValidatorBeCalled(HttpBaseMethod method, int status) { - do { - String requestId = RandomUtils.generateRandomUUID(); - - // Header to allow tracing requests in apache and ownCloud logs - Timber.d("Executing in request with id %s", requestId); - method.setRequestHeader(OC_X_REQUEST_ID, requestId); - method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent()); - method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY); - if (mCredentials.getHeaderAuth() != null) { - method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth()); - } - status = method.execute(); - - repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter); - if (repeatWithFreshCredentials) { - repeatCounter++; - } - } while (repeatWithFreshCredentials); - - return status; - } - - public RedirectionPath followRedirection(HttpBaseMethod method) throws Exception { - int redirectionsCount = 0; - int status = method.getStatusCode(); - RedirectionPath redirectionPath = new RedirectionPath(status, MAX_REDIRECTIONS_COUNT); - - while (redirectionsCount < MAX_REDIRECTIONS_COUNT && - (status == HttpConstants.HTTP_MOVED_PERMANENTLY || - status == HttpConstants.HTTP_MOVED_TEMPORARILY || - status == HttpConstants.HTTP_TEMPORARY_REDIRECT) - ) { - - final String location = method.getResponseHeader(HttpConstants.LOCATION_HEADER) != null - ? method.getResponseHeader(HttpConstants.LOCATION_HEADER) - : method.getResponseHeader(HttpConstants.LOCATION_HEADER_LOWER); - - if (location != null) { - Timber.d("#" + mInstanceNumber + "Location to redirect: " + location); - - redirectionPath.addLocation(location); - - // Release the connection to avoid reach the max number of connections per host - // due to it will be set a different url - exhaustResponse(method.getResponseBodyAsStream()); - - method.setUrl(HttpUrl.parse(location)); - final String destination = method.getRequestHeader("Destination") != null - ? method.getRequestHeader("Destination") - : method.getRequestHeader("destination"); - - if (destination != null) { - final int suffixIndex = location.lastIndexOf(getUserFilesWebDavUri().toString()); - final String redirectionBase = location.substring(0, suffixIndex); - final String destinationPath = destination.substring(mBaseUri.toString().length()); - - method.setRequestHeader("destination", redirectionBase + destinationPath); - } - try { - status = executeRedirectedHttpMethod(method); - } catch (HttpException e) { - if (e.getMessage().contains(Integer.toString(HttpConstants.HTTP_MOVED_TEMPORARILY))) { - status = HttpConstants.HTTP_MOVED_TEMPORARILY; - } else { - throw e; - } - } - redirectionPath.addStatus(status); - redirectionsCount++; - - } else { - Timber.d(" #" + mInstanceNumber + "No location to redirect!"); - status = HttpConstants.HTTP_NOT_FOUND; - } - } - return redirectionPath; + return mConnectionValidator != null && ( + (!(mCredentials instanceof OwnCloudAnonymousCredentials) && + status == HttpConstants.HTTP_UNAUTHORIZED + ) || (!mFollowRedirects && + !method.getFollowRedirects() && + status == HttpConstants.HTTP_MOVED_TEMPORARILY + ) + ); } /** @@ -273,26 +229,18 @@ public class OwnCloudClient extends HttpClient { } } - public String getCookiesString() { - StringBuilder cookiesString = new StringBuilder(); - List cookieList = getCookiesFromUrl(HttpUrl.parse(mBaseUri.toString())); - - if (cookieList != null) { - for (Cookie cookie : cookieList) { - cookiesString.append(cookie.toString()).append(";"); - } - } - - return cookiesString.toString(); - } - - public void setCookiesForCurrentAccount(List 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; } @@ -309,95 +257,7 @@ 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)) { - boolean invalidated = invalidateAccountCredentials(); - - if (invalidated) { - if (getCredentials().authTokenCanBeRefreshed() && - repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) { - try { - mAccount.loadCredentials(getContext()); - // if mAccount.getCredentials().length() == 0 --> refresh failed - setCredentials(mAccount.getCredentials()); - credentialsWereRefreshed = true; - - } catch (AccountsException | IOException e) { - Timber.e(e, "Error while trying to refresh auth token for %s", - mAccount.getSavedAccount().name - ); - } - } - - if (!credentialsWereRefreshed && mSingleSessionManager != null) { - // if credentials are not refreshed, client must be removed - // from the OwnCloudClientManager to prevent it is reused once and again - mSingleSessionManager.removeClientFor(mAccount); - } - } - // else: onExecute will finish with status 401 - } - - return credentialsWereRefreshed; - } - - /** - * Determines if credentials should be invalidated according the to the HTTPS status - * of a network request just performed. - * - * @param httpStatusCode Result of the last request ran with the 'credentials' belows. - * @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or - * cannot be invalidated with the given arguments. - */ - private boolean shouldInvalidateAccountCredentials(int httpStatusCode) { - boolean shouldInvalidateAccountCredentials = - (httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED); - - shouldInvalidateAccountCredentials &= (mCredentials != null && // real credentials - !(mCredentials instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials)); - - // test if have all the needed to effectively invalidate ... - shouldInvalidateAccountCredentials &= (mAccount != null && mAccount.getSavedAccount() != null && getContext() != null); - - return shouldInvalidateAccountCredentials; - } - - /** - * Invalidates credentials stored for the given account in the system {@link AccountManager} and in - * current {@link SingleSessionManager#getDefaultSingleton()} instance. - *

- * {@link #shouldInvalidateAccountCredentials(int)} should be called first. - * - * @return 'True' if invalidation was successful, 'false' otherwise. - */ - private boolean invalidateAccountCredentials() { - AccountManager am = AccountManager.get(getContext()); - am.invalidateAuthToken( - mAccount.getSavedAccount().type, - mCredentials.getAuthToken() - ); - am.clearPassword(mAccount.getSavedAccount()); // being strict, only needed for Basic Auth credentials - return true; - } - - public boolean followRedirects() { - return mFollowRedirects; - } - public void setFollowRedirects(boolean followRedirects) { this.mFollowRedirects = followRedirects; } -} +} \ No newline at end of file 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..7e40c16f 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,7 +77,23 @@ public class SingleSessionManager { sUserAgent = userAgent; } - public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws OperationCanceledException, + private static OwnCloudClient createOwnCloudClient(Uri uri, + Context context, + ConnectionValidator connectionValidator, + SingleSessionManager singleSessionManager) { + OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true, singleSessionManager, context); + return client; + } + + public OwnCloudClient getClientFor(OwnCloudAccount account, + Context context) throws OperationCanceledException, + AuthenticatorException, IOException { + return getClientFor(account, context, getConnectionValidator()); + } + + public OwnCloudClient getClientFor(OwnCloudAccount account, + Context context, + ConnectionValidator connectionValidator) throws OperationCanceledException, AuthenticatorException, IOException { Timber.d("getClientFor starting "); @@ -104,10 +129,11 @@ public class SingleSessionManager { if (client == null) { // no client to reuse - create a new one - client = OwnCloudClientFactory.createOwnCloudClient( + client = createOwnCloudClient( account.getBaseUri(), - context.getApplicationContext(), - true); // TODO remove dependency on OwnCloudClientFactory + context, + connectionValidator, + this); // TODO remove dependency on OwnCloudClientFactory //the next two lines are a hack because okHttpclient is used as a singleton instead of being an //injected instance that can be deleted when required @@ -115,7 +141,6 @@ public class SingleSessionManager { client.clearCredentials(); client.setAccount(account); - HttpClient.setContext(context); account.loadCredentials(context); client.setCredentials(account.getCredentials()); 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/sample_client/src/main/java/com/owncloud/android/lib/sampleclient/FilesArrayAdapter.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/DummyInterceptor.kt similarity index 63% rename from sample_client/src/main/java/com/owncloud/android/lib/sampleclient/FilesArrayAdapter.java rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/DummyInterceptor.kt index 29219f21..8a81b460 100644 --- a/sample_client/src/main/java/com/owncloud/android/lib/sampleclient/FilesArrayAdapter.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/DummyInterceptor.kt @@ -21,26 +21,11 @@ * THE SOFTWARE. * */ -package com.owncloud.android.lib.sampleclient; -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; +package com.owncloud.android.lib.common.http -import com.owncloud.android.lib.resources.files.RemoteFile; - -public class FilesArrayAdapter extends ArrayAdapter { - - public FilesArrayAdapter(Context context, int resource) { - super(context, resource); - } - - public View getView(int position, View convertView, ViewGroup parent) { - TextView textView = (TextView) super.getView(position, convertView, parent); - textView.setText(getItem(position).getRemotePath()); - return textView; - } -} +import okhttp3.Interceptor +class DummyInterceptor : Interceptor { + override fun intercept(chain: Interceptor.Chain) = chain.proceed(chain.request()) +} \ 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 046dc711..20539413 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 @@ -40,7 +40,6 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.HashMap; @@ -54,32 +53,46 @@ import java.util.concurrent.TimeUnit; */ public class HttpClient { - private static OkHttpClient sOkHttpClient; - private static Context sContext; - private static HashMap> sCookieStore = new HashMap<>(); - private static LogInterceptor sLogInterceptor; + private Context mContext; + private HashMap> mCookieStore = new HashMap<>(); + private LogInterceptor mLogInterceptor = new LogInterceptor(); - public static OkHttpClient getOkHttpClient() { - if (sOkHttpClient == null) { - try { - final X509TrustManager trustManager = new AdvancedX509TrustManager( - NetworkUtils.getKnownServersStore(sContext)); - final SSLSocketFactory sslSocketFactory = getNewSslSocketFactory(trustManager); - // Automatic cookie handling, NOT PERSISTENT - final CookieJar cookieJar = new CookieJarImpl(sCookieStore); + private OkHttpClient mOkHttpClient = null; - // TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok. - //.hostnameVerifier(new BrowserCompatHostnameVerifier()); - sOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar); - - } catch (Exception e) { - Timber.e(e, "Could not setup SSL system."); - } + protected HttpClient(Context context) { + if (context == null) { + Timber.e("Context may not be NULL!"); + throw new NullPointerException("Context may not be NULL!"); } - return sOkHttpClient; + mContext = context; } - private static SSLContext getSslContext() throws NoSuchAlgorithmException { + public OkHttpClient getOkHttpClient() { + if (mOkHttpClient == null) { + try { + final X509TrustManager trustManager = new AdvancedX509TrustManager( + NetworkUtils.getKnownServersStore(mContext)); + + final SSLContext sslContext = buildSSLContext(); + sslContext.init(null, new TrustManager[]{trustManager}, null); + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + // Automatic cookie handling, NOT PERSISTENT + final CookieJar cookieJar = new CookieJarImpl(mCookieStore); + mOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar); + + } catch (NoSuchAlgorithmException nsae) { + Timber.e(nsae, "Could not setup SSL system."); + throw new RuntimeException("Could not setup okHttp client.", nsae); + } catch (Exception e) { + Timber.e(e, "Could not setup okHttp client."); + throw new RuntimeException("Could not setup okHttp client.", e); + } + } + return mOkHttpClient; + } + + private SSLContext buildSSLContext() throws NoSuchAlgorithmException { try { return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName()); } catch (NoSuchAlgorithmException tlsv13Exception) { @@ -100,17 +113,11 @@ public class HttpClient { } } - private static SSLSocketFactory getNewSslSocketFactory(X509TrustManager trustManager) - throws NoSuchAlgorithmException, KeyManagementException { - final SSLContext sslContext = getSslContext(); - sslContext.init(null, new TrustManager[]{trustManager}, null); - return sslContext.getSocketFactory(); - } - - private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager, - CookieJar cookieJar) { + private OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager, + CookieJar cookieJar) { return new OkHttpClient.Builder() .addNetworkInterceptor(getLogInterceptor()) + .addNetworkInterceptor(DebugInterceptorFactory.INSTANCE.getInterceptor()) .protocols(Collections.singletonList(Protocol.HTTP_1_1)) .readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) .writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) @@ -122,26 +129,19 @@ public class HttpClient { .build(); } - public static LogInterceptor getLogInterceptor() { - if (sLogInterceptor == null) { - sLogInterceptor = new LogInterceptor(); - } - return sLogInterceptor; - } - - public static List getCookiesFromUrl(HttpUrl httpUrl) { - return sCookieStore.get(httpUrl.host()); - } - public Context getContext() { - return sContext; + return mContext; } - public static void setContext(Context context) { - sContext = context; + public LogInterceptor getLogInterceptor() { + return mLogInterceptor; + } + + public List getCookiesFromUrl(HttpUrl httpUrl) { + return mCookieStore.get(httpUrl.host()); } public void clearCookies() { - sCookieStore.clear(); + mCookieStore.clear(); } } 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 0c4c7ada..f446675e 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 @@ -1,3 +1,27 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + package com.owncloud.android.lib.common.http.methods import com.owncloud.android.lib.common.http.HttpClient @@ -14,23 +38,41 @@ import java.net.URL import java.util.concurrent.TimeUnit abstract class HttpBaseMethod constructor(url: URL) { - var okHttpClient: OkHttpClient var httpUrl: HttpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException() var request: Request + var followPermanentRedirects = false abstract var response: Response - var call: Call? = null + var followRedirects: Boolean = true + var retryOnConnectionFailure: Boolean = true + var connectionTimeoutVal: Long? = null + var connectionTimeoutUnit: TimeUnit? = null + var readTimeoutVal: Long? = null + private set + var readTimeoutUnit: TimeUnit? = null + private set + init { - okHttpClient = HttpClient.getOkHttpClient() request = Request.Builder() .url(httpUrl) .build() } @Throws(Exception::class) - open fun execute(): Int { - return onExecute() + open fun execute(httpClient: HttpClient): Int { + val okHttpClient = httpClient.okHttpClient.newBuilder().apply { + retryOnConnectionFailure(retryOnConnectionFailure) + followRedirects(followRedirects) + readTimeoutUnit?.let { unit -> + readTimeoutVal?.let { readTimeout(it, unit) } + } + connectionTimeoutUnit?.let { unit -> + connectionTimeoutVal?.let { connectTimeout(it, unit) } + } + }.build() + + return onExecute(okHttpClient) } open fun setUrl(url: HttpUrl) { @@ -99,6 +141,11 @@ abstract class HttpBaseMethod constructor(url: URL) { return response.body?.byteStream() } + /** + * returns the final url after following the last redirect. + */ + open fun getFinalUrl() = response.request.url + /************************* *** Connection Params *** *************************/ @@ -107,31 +154,19 @@ abstract class HttpBaseMethod constructor(url: URL) { // Setter ////////////////////////////// // Connection parameters - open fun setRetryOnConnectionFailure(retryOnConnectionFailure: Boolean) { - okHttpClient = okHttpClient.newBuilder() - .retryOnConnectionFailure(retryOnConnectionFailure) - .build() - } + open fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) { - okHttpClient = okHttpClient.newBuilder() - .readTimeout(readTimeout, timeUnit) - .build() + readTimeoutVal = readTimeout + readTimeoutUnit = timeUnit } open fun setConnectionTimeout( connectionTimeout: Long, timeUnit: TimeUnit ) { - okHttpClient = okHttpClient.newBuilder() - .readTimeout(connectionTimeout, timeUnit) - .build() - } - - open fun setFollowRedirects(followRedirects: Boolean) { - okHttpClient = okHttpClient.newBuilder() - .followRedirects(followRedirects) - .build() + connectionTimeoutVal = connectionTimeout + connectionTimeoutUnit = timeUnit } /************ @@ -148,5 +183,5 @@ abstract class HttpBaseMethod constructor(url: URL) { // For override ////////////////////////////// @Throws(Exception::class) - protected abstract fun onExecute(): Int + protected abstract fun onExecute(okHttpClient: OkHttpClient): Int } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/DeleteMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/DeleteMethod.kt index 51069bc2..8ae30904 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/DeleteMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/DeleteMethod.kt @@ -23,6 +23,7 @@ */ package com.owncloud.android.lib.common.http.methods.nonwebdav +import okhttp3.OkHttpClient import java.io.IOException import java.net.URL @@ -33,10 +34,10 @@ import java.net.URL */ class DeleteMethod(url: URL) : HttpMethod(url) { @Throws(IOException::class) - override fun onExecute(): Int { + override fun onExecute(okHttpClient: OkHttpClient): Int { request = request.newBuilder() .delete() .build() - return super.onExecute() + return super.onExecute(okHttpClient) } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/GetMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/GetMethod.kt index 65bf40fc..d24c3c88 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/GetMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/GetMethod.kt @@ -23,6 +23,7 @@ */ package com.owncloud.android.lib.common.http.methods.nonwebdav +import okhttp3.OkHttpClient import java.io.IOException import java.net.URL @@ -33,10 +34,10 @@ import java.net.URL */ class GetMethod(url: URL) : HttpMethod(url) { @Throws(IOException::class) - override fun onExecute(): Int { + override fun onExecute(okHttpClient: OkHttpClient): Int { request = request.newBuilder() .get() .build() - return super.onExecute() + return super.onExecute(okHttpClient) } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/HttpMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/HttpMethod.kt index da759ada..c91f4228 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/HttpMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/HttpMethod.kt @@ -24,6 +24,7 @@ package com.owncloud.android.lib.common.http.methods.nonwebdav import com.owncloud.android.lib.common.http.methods.HttpBaseMethod +import okhttp3.OkHttpClient import okhttp3.Response import java.net.URL @@ -38,7 +39,7 @@ abstract class HttpMethod( override lateinit var response: Response - public override fun onExecute(): Int { + public override fun onExecute(okHttpClient: OkHttpClient): Int { call = okHttpClient.newCall(request) call?.let { response = it.execute() } return super.statusCode diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/PostMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/PostMethod.kt index ba4f6d4d..5b51c32f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/PostMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/PostMethod.kt @@ -23,6 +23,7 @@ */ package com.owncloud.android.lib.common.http.methods.nonwebdav +import okhttp3.OkHttpClient import okhttp3.RequestBody import java.io.IOException import java.net.URL @@ -37,10 +38,10 @@ class PostMethod( private val postRequestBody: RequestBody ) : HttpMethod(url) { @Throws(IOException::class) - override fun onExecute(): Int { + override fun onExecute(okHttpClient: OkHttpClient): Int { request = request.newBuilder() .post(postRequestBody) .build() - return super.onExecute() + return super.onExecute(okHttpClient) } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/PutMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/PutMethod.kt index 98be1503..91723b69 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/PutMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/nonwebdav/PutMethod.kt @@ -23,6 +23,7 @@ */ package com.owncloud.android.lib.common.http.methods.nonwebdav +import okhttp3.OkHttpClient import okhttp3.RequestBody import java.io.IOException import java.net.URL @@ -37,10 +38,10 @@ class PutMethod( private val putRequestBody: RequestBody ) : HttpMethod(url) { @Throws(IOException::class) - override fun onExecute(): Int { + override fun onExecute(okHttpClient: OkHttpClient): Int { request = request.newBuilder() .put(putRequestBody) .build() - return super.onExecute() + return super.onExecute(okHttpClient) } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/CopyMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/CopyMethod.kt index 2c816d75..d10718e0 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/CopyMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/CopyMethod.kt @@ -23,6 +23,7 @@ */ package com.owncloud.android.lib.common.http.methods.webdav +import at.bitfire.dav4jvm.DavOCResource import okhttp3.Response import java.net.URL @@ -38,7 +39,7 @@ class CopyMethod( private val forceOverride: Boolean ) : DavMethod(url) { @Throws(Exception::class) - public override fun onExecute(): Int { + public override fun onDavExecute(davResource: DavOCResource): Int { davResource.copy( destinationUrl, forceOverride, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/DavMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/DavMethod.kt index b9bc14cd..100177ec 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/DavMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/DavMethod.kt @@ -29,14 +29,11 @@ import at.bitfire.dav4jvm.exception.HttpException import at.bitfire.dav4jvm.exception.RedirectException import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.methods.HttpBaseMethod -import okhttp3.HttpUrl -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.OkHttpClient import okhttp3.Protocol import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody -import java.net.MalformedURLException import java.net.URL -import java.util.concurrent.TimeUnit /** * Wrapper to perform WebDAV (dav4android) calls @@ -44,27 +41,25 @@ import java.util.concurrent.TimeUnit * @author David González Verdugo */ abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) { - protected var davResource: DavOCResource - override lateinit var response: Response - - init { - val httpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException() - davResource = DavOCResource( - okHttpClient, - httpUrl, - log - ) - } + private var davResource: DavOCResource? = null override fun abort() { - davResource.cancelCall() + davResource?.cancelCall() } + protected abstract fun onDavExecute(davResource: DavOCResource): Int + @Throws(Exception::class) - override fun execute(): Int { + override fun onExecute(okHttpClient: OkHttpClient): Int { return try { - onExecute() + davResource = DavOCResource( + okHttpClient.newBuilder().followRedirects(false).build(), + httpUrl, + log + ) + + onDavExecute(davResource!!) } catch (httpException: HttpException) { // Modify responses with information gathered from exceptions if (httpException is RedirectException) { @@ -91,71 +86,12 @@ abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) { } } - ////////////////////////////// - // Setter - ////////////////////////////// - // Connection parameters - override fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) { - super.setReadTimeout(readTimeout, timeUnit) - davResource = DavOCResource( - okHttpClient, - request.url, - log - ) - } - - override fun setConnectionTimeout( - connectionTimeout: Long, - timeUnit: TimeUnit - ) { - super.setConnectionTimeout(connectionTimeout, timeUnit) - davResource = DavOCResource( - okHttpClient, - request.url, - log - ) - } - - override fun setFollowRedirects(followRedirects: Boolean) { - super.setFollowRedirects(followRedirects) - davResource = DavOCResource( - okHttpClient, - request.url, - log - ) - } - - override fun setUrl(url: HttpUrl) { - super.setUrl(url) - davResource = DavOCResource( - okHttpClient, - request.url, - log - ) - } - - override fun setRequestHeader(name: String, value: String) { - super.setRequestHeader(name, value) - davResource = DavOCResource( - okHttpClient, - request.url, - log - ) - } - ////////////////////////////// // Getter ////////////////////////////// - override fun setRetryOnConnectionFailure(retryOnConnectionFailure: Boolean) { - super.setRetryOnConnectionFailure(retryOnConnectionFailure) - davResource = DavOCResource( - okHttpClient, - request.url, - log - ) - } + override val isAborted: Boolean - get() = davResource.isCallAborted() + get() = davResource?.isCallAborted() ?: false } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MkColMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MkColMethod.kt index 03c8aff0..8d77cd3a 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MkColMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MkColMethod.kt @@ -23,6 +23,7 @@ */ package com.owncloud.android.lib.common.http.methods.webdav +import at.bitfire.dav4jvm.DavOCResource import okhttp3.Response import java.net.URL @@ -34,7 +35,7 @@ import java.net.URL */ class MkColMethod(url: URL) : DavMethod(url) { @Throws(Exception::class) - public override fun onExecute(): Int { + public override fun onDavExecute(davResource: DavOCResource): Int { davResource.mkCol( xmlBody = null, listOfHeaders = super.getRequestHeadersAsHashMap() diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MoveMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MoveMethod.kt index 4c65cb98..bc6d9d3e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MoveMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MoveMethod.kt @@ -23,6 +23,7 @@ */ package com.owncloud.android.lib.common.http.methods.webdav +import at.bitfire.dav4jvm.DavOCResource import okhttp3.Response import java.net.URL @@ -38,7 +39,7 @@ class MoveMethod( private val forceOverride: Boolean ) : DavMethod(url) { @Throws(Exception::class) - public override fun onExecute(): Int { + override fun onDavExecute(davResource: DavOCResource): Int { davResource.move( destinationUrl, forceOverride, 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..6bce3472 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 @@ -23,6 +23,7 @@ */ package com.owncloud.android.lib.common.http.methods.webdav +import at.bitfire.dav4jvm.DavOCResource import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.Response.HrefRelation @@ -47,10 +48,10 @@ class PropfindMethod( private set @Throws(IOException::class, DavException::class) - public override fun onExecute(): Int { + public override fun onDavExecute(davResource: DavOCResource): Int { davResource.propfind( depth = depth, - reqProp = *propertiesToRequest, + reqProp = propertiesToRequest, listOfHeaders = super.getRequestHeadersAsHashMap(), callback = { response: Response, hrefRelation: HrefRelation? -> when (hrefRelation) { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PutMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PutMethod.kt index 70dea1de..60330a3a 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PutMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PutMethod.kt @@ -23,6 +23,7 @@ */ package com.owncloud.android.lib.common.http.methods.webdav +import at.bitfire.dav4jvm.DavOCResource import at.bitfire.dav4jvm.exception.HttpException import com.owncloud.android.lib.common.http.HttpConstants import okhttp3.RequestBody @@ -39,7 +40,7 @@ class PutMethod( private val putRequestBody: RequestBody ) : DavMethod(url) { @Throws(IOException::class, HttpException::class) - public override fun onExecute(): Int { + public override fun onDavExecute(davResource: DavOCResource): Int { davResource.put( putRequestBody, super.getRequestHeader(HttpConstants.IF_MATCH_HEADER), 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 ef2a8321..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 @@ -1,3 +1,27 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + package com.owncloud.android.lib.common.operations; import android.accounts.Account; @@ -135,7 +159,7 @@ public abstract class RemoteOperation 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)"); @@ -265,4 +289,4 @@ public abstract class RemoteOperation implements Runnable { mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend); } } -} \ No newline at end of file +} 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 64386964..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 @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. + * Copyright (C) 2022 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,6 +34,7 @@ import com.owncloud.android.lib.common.http.HttpConstants; import com.owncloud.android.lib.common.http.methods.HttpBaseMethod; import com.owncloud.android.lib.common.network.CertificateCombinedException; import okhttp3.Headers; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.json.JSONException; import timber.log.Timber; @@ -59,13 +60,15 @@ public class RemoteOperationResult * Generated - should be refreshed every time the class changes!! */ private static final long serialVersionUID = 4968939884332372230L; + private static final String LOCATION = "location"; + private static final String WWW_AUTHENTICATE = "www-authenticate"; private boolean mSuccess = false; private int mHttpCode = -1; private String mHttpPhrase = null; private Exception mException = null; private ResultCode mCode = ResultCode.UNKNOWN_ERROR; - private String mRedirectedLocation; + private String mRedirectedLocation = ""; private List mAuthenticate = new ArrayList<>(); private String mLastPermanentLocation = null; private T mData = null; @@ -112,6 +115,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; @@ -248,11 +259,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()); } @@ -321,7 +332,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/common/utils/LoggingHelper.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/LoggingHelper.kt index f24e5e34..9924c553 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/LoggingHelper.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/LoggingHelper.kt @@ -1,3 +1,27 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + package com.owncloud.android.lib.common.utils import info.hannes.timber.FileLoggingTree diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/Service.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/Service.kt index 7b4a62df..586ae6ac 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/Service.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/Service.kt @@ -1,21 +1,27 @@ -/** - * ownCloud Android client application +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. * - * @author David González Verdugo + * @author David González Verdugo * - * Copyright (C) 2020 ownCloud GmbH. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *

- * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ package com.owncloud.android.lib.resources 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..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 @@ -44,26 +43,18 @@ import java.util.concurrent.TimeUnit * @author Abel García de Prada * * @param remotePath Path to append to the URL owned by the client instance. - * @param isUserLogged When `true`, the username won't be added at the end of the PROPFIND url since is not + * @param isUserLoggedIn When `true`, the username won't be added at the end of the PROPFIND url since is not * needed to check user credentials */ class CheckPathExistenceRemoteOperation( val remotePath: String? = "", - val isUserLogged: Boolean + val isUserLoggedIn: Boolean ) : RemoteOperation() { - /** - * 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.followRedirects() return try { val stringUrl = - if (isUserLogged) client.baseFilesWebDavUri.toString() + if (isUserLoggedIn) client.baseFilesWebDavUri.toString() else client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath) val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropset).apply { @@ -71,12 +62,7 @@ class CheckPathExistenceRemoteOperation( setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) } - client.setFollowRedirects(false) var status = client.executeHttpMethod(propFindMethod) - if (previousFollowRedirects) { - redirectionPath = client.followRedirection(propFindMethod) - status = redirectionPath?.lastStatus!! - } /* PROPFIND method * 404 NOT FOUND: path doesn't exist, * 207 MULTI_STATUS: path exists. @@ -94,16 +80,9 @@ class CheckPathExistenceRemoteOperation( "Existence check for ${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)} : ${result.logMessage}" ) result - } finally { - client.setFollowRedirects(previousFollowRedirects) } } - /** - * @return 'True' if the operation was executed and at least one redirection was followed. - */ - fun wasRedirected() = redirectionPath?.redirectionsCount ?: 0 > 0 - private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS companion object { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.java index 2f9e5431..9c78425a 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.java @@ -92,7 +92,8 @@ public class CopyRemoteFileOperation extends RemoteOperation { RemoteOperationResult result; try { CopyMethod copyMethod = - new CopyMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mSrcRemotePath)), + new CopyMethod( + new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mSrcRemotePath)), client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath), mOverwrite); diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java index 3985ab2c..302df20d 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java @@ -87,7 +87,8 @@ public class CreateRemoteFolderOperation extends RemoteOperation { RemoteOperationResult result; try { Uri webDavUri = createChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri(); - final MkColMethod mkcol = new MkColMethod(new URL(webDavUri + WebdavUtils.encodePath(mRemotePath))); + final MkColMethod mkcol = new MkColMethod( + new URL(webDavUri + WebdavUtils.encodePath(mRemotePath))); mkcol.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS); mkcol.setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS); final int status = client.executeHttpMethod(mkcol); 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..904a9bea --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt @@ -0,0 +1,77 @@ +/* ownCloud Android Library is available under MIT license +* Copyright (C) 2022 ownCloud GmbH. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +* +*/ +package com.owncloud.android.lib.resources.files + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.webdav.DavUtils +import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import timber.log.Timber +import java.net.URL +import java.util.concurrent.TimeUnit + +/** + * Operation to get the base url, which might differ in case of a redirect. + * + * @author Christian Schabesberger + */ + +class GetBaseUrlRemoteOperation : RemoteOperation() { + override fun run(client: OwnCloudClient): RemoteOperationResult { + return try { + val stringUrl = client.baseFilesWebDavUri.toString() + + val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropset).apply { + setReadTimeout(TIMEOUT, TimeUnit.SECONDS) + setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS) + } + + val status = client.executeHttpMethod(propFindMethod) + + if (isSuccess(status)) { + RemoteOperationResult(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 = 10_000L + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java index 9149e6d3..24496ebe 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java @@ -76,7 +76,8 @@ public class ReadRemoteFileOperation extends RemoteOperation { /// take the duty of check the server for the current state of the file there try { // remote request - propfind = new PropfindMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), + propfind = new PropfindMethod( + new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), DEPTH_0, DavUtils.getAllPropset()); 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(ResultCode.INVALID_OVERWRITE); } - final MoveMethod move = new MoveMethod(new URL(client.getUserFilesWebDavUri() + + final MoveMethod move = new MoveMethod( + new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mOldRemotePath)), client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mNewRemotePath), false); diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromContentUriOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromContentUriOperation.kt index 9a1e1b19..dfcef34d 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromContentUriOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromContentUriOperation.kt @@ -50,7 +50,7 @@ class UploadFileFromContentUriOperation( override fun run(client: OwnCloudClient): RemoteOperationResult { val putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(uploadPath)), requestBody).apply { - setRetryOnConnectionFailure(false) + retryOnConnectionFailure = false addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, requestBody.contentLength().toString()) addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, lastModified) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadRemoteFileOperation.java index 84396ec8..10b8ea36 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadRemoteFileOperation.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadRemoteFileOperation.java @@ -92,7 +92,8 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation result = new RemoteOperationResult<>(new OperationCancelledException()); break; } else { - mPutMethod = new PutMethod(new URL(uriPrefix + File.separator + chunkIndex), mFileRequestBody); + mPutMethod = new PutMethod( + new URL(uriPrefix + File.separator + chunkIndex), mFileRequestBody); if (chunkIndex == chunkCount - 1) { // Added a high timeout to the last chunk due to when the last chunk 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 abe1e2e6..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 @@ -1,22 +1,26 @@ -/** - * ownCloud Android client application +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. * - * @author Abel García de Prada - * Copyright (C) 2020 ownCloud GmbH. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ - package com.owncloud.android.lib.resources.files.services.implementation import com.owncloud.android.lib.common.OwnCloudClient @@ -29,6 +33,6 @@ class OCFileService(override val client: OwnCloudClient) : override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult = CheckPathExistenceRemoteOperation( remotePath = path, - isUserLogged = isUserLogged + isUserLoggedIn = isUserLogged ).execute(client) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/oauth/GetOIDCDiscoveryRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/oauth/GetOIDCDiscoveryRemoteOperation.kt index 9cebcf44..c3f55dc9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/oauth/GetOIDCDiscoveryRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/oauth/GetOIDCDiscoveryRemoteOperation.kt @@ -55,6 +55,7 @@ class GetOIDCDiscoveryRemoteOperation : RemoteOperation() addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) } + getMethod.followRedirects = true val status = client.executeHttpMethod(getMethod) val responseBody = getMethod.getResponseBodyAsString() diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.kt index 1f043cd1..ed2b7417 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.kt @@ -138,7 +138,6 @@ class CreateRemoteShareOperation( } private fun createFormBody(): FormBody { - val formBodyBuilder = FormBody.Builder() .add(PARAM_PATH, remoteFilePath) .add(PARAM_SHARE_TYPE, shareType.value.toString()) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt index 2ce20a2c..304ead67 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt @@ -104,6 +104,7 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper override fun run(client: OwnCloudClient): RemoteOperationResult { + val requestUri = buildRequestUri(client.baseUri) val deleteMethod = DeleteMethod(URL(requestUri.toString())).apply { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt index 73c331ad..a6cc71e3 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt @@ -195,6 +195,7 @@ class UpdateRemoteShareOperation return formBodyBuilder } + override fun run(client: OwnCloudClient): RemoteOperationResult { val requestUri = buildRequestUri(client.baseUri) @@ -205,7 +206,6 @@ class UpdateRemoteShareOperation addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) } - return try { val status = client.executeHttpMethod(putMethod) val response = putMethod.getResponseBodyAsString() diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt index 728217bb..9774ca38 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt @@ -1,21 +1,27 @@ -/** - * ownCloud Android client application +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. * - * @author David González Verdugo + * @author David González Verdugo * - * Copyright (C) 2020 ownCloud GmbH. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ package com.owncloud.android.lib.resources.shares.services diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareeService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareeService.kt index 95983ec5..4023a02b 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareeService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareeService.kt @@ -1,22 +1,28 @@ -/** - * ownCloud Android client application +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. * - * @author Christian Schabesberger - * @author David González Verdugo + * @author Christian Schabesberger + * @author David González Verdugo * - * Copyright (C) 2020 ownCloud GmbH. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ package com.owncloud.android.lib.resources.shares.services diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt index 0d9f36b1..f8bcd1ea 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt @@ -1,21 +1,27 @@ -/** - * ownCloud Android client application +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. * - * @author David González Verdugo + * @author David González Verdugo * - * Copyright (C) 2020 ownCloud GmbH. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *

- * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ package com.owncloud.android.lib.resources.shares.services.implementation diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareeService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareeService.kt index 9cb014f3..513de693 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareeService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareeService.kt @@ -1,21 +1,26 @@ -/** - * ownCloud Android client application + /* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. * - * @author David González Verdugo + * @author David González Verdugo + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * Copyright (C) 2020 ownCloud GmbH. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *

- * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ package com.owncloud.android.lib.resources.shares.services.implementation 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..3be9c82b 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,27 +43,25 @@ import timber.log.Timber class GetRemoteStatusOperation : RemoteOperation() { public override fun run(client: OwnCloudClient): RemoteOperationResult { - 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) + if (!usesHttpOrHttps(client.baseUri)) { + client.baseUri = buildFullHttpsUrl(client.baseUri) } - - return result + return tryToConnect(client) } + private fun updateClientBaseUrl(client: OwnCloudClient, newBaseUrl: String) { + client.baseUri = Uri.parse(newBaseUrl) + } + + 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) + 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 f25086f3..9c246e9a 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. @@ -73,36 +73,18 @@ internal class StatusRequester { data class RequestResult( val getMethod: GetMethod, val status: Int, - val redirectedToUnsecureLocation: Boolean, val lastLocation: String ) - fun requestAndFollowRedirects(baseLocation: String, client: OwnCloudClient): RequestResult { - var currentLocation = baseLocation + OwnCloudClient.STATUS_PATH - var redirectedToUnsecureLocation = false + fun request(baseLocation: String, client: OwnCloudClient): RequestResult { + val currentLocation = baseLocation + OwnCloudClient.STATUS_PATH var status: Int + val getMethod = getGetMethod(currentLocation) - while (true) { - val getMethod = getGetMethod(currentLocation) + getMethod.followPermanentRedirects = true + status = client.executeHttpMethod(getMethod) - 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 - } - } + return RequestResult(getMethod, status, getMethod.getFinalUrl().toString()) } private fun Int.isSuccess() = this == HttpConstants.HTTP_OK @@ -122,12 +104,8 @@ internal class StatusRequester { // the version object will be returned even if the version is invalid, no error code; // every app will decide how to act if (ocVersion.isVersionValid() == false) val result: RemoteOperationResult = - 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, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/CapabilityService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/CapabilityService.kt index ae75181b..52812b47 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/CapabilityService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/CapabilityService.kt @@ -1,21 +1,27 @@ -/** - * ownCloud Android client application +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. * - * @author David González Verdugo + * @author David González Verdugo * - * Copyright (C) 2020 ownCloud GmbH. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ package com.owncloud.android.lib.resources.status.services diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/ServerInfoService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/ServerInfoService.kt index 911b2b80..b1eb2871 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/ServerInfoService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/ServerInfoService.kt @@ -23,12 +23,13 @@ */ package com.owncloud.android.lib.resources.status.services + import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.status.RemoteServerInfo interface ServerInfoService { - fun checkPathExistence(path: String, isUserLogged: Boolean, client: OwnCloudClient): RemoteOperationResult + fun checkPathExistence(path: String, isUserLoggedIn: Boolean, client: OwnCloudClient): RemoteOperationResult fun getRemoteStatus(path: String, client: OwnCloudClient): RemoteOperationResult } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCCapabilityService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCCapabilityService.kt index 8d8c9d95..e211475f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCCapabilityService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCCapabilityService.kt @@ -1,21 +1,27 @@ -/** - * ownCloud Android client application +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. * - * @author David González Verdugo + * @author David González Verdugo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * Copyright (C) 2020 ownCloud GmbH. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ package com.owncloud.android.lib.resources.status.services.implementation 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 0dbae093..d2c891f0 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCServerInfoService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/implementation/OCServerInfoService.kt @@ -1,24 +1,32 @@ -/** - * ownCloud Android client application +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. * - * @author Abel García de Prada - * Copyright (C) 2020 ownCloud GmbH. + * @author Abel García de Prada * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ package com.owncloud.android.lib.resources.status.services.implementation + import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation @@ -30,12 +38,12 @@ class OCServerInfoService : ServerInfoService { override fun checkPathExistence( path: String, - isUserLogged: Boolean, + isUserLoggedIn: Boolean, client: OwnCloudClient ): RemoteOperationResult = CheckPathExistenceRemoteOperation( remotePath = path, - isUserLogged = true + isUserLoggedIn = true ).execute(client) override fun getRemoteStatus( diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/implementation/OCUserService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/implementation/OCUserService.kt index d21d2909..6e3c20c0 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/implementation/OCUserService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/implementation/OCUserService.kt @@ -1,20 +1,27 @@ -/** - * ownCloud Android client application +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. * - * @author Abel García de Prada - * Copyright (C) 2020 ownCloud GmbH. + * @author Abel García de Prada * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *

- * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ package com.owncloud.android.lib.resources.users.services.implementation diff --git a/owncloudComLibrary/src/release/java/com/owncloud/android/lib/common/http/DebugInterceptorFactory.kt b/owncloudComLibrary/src/release/java/com/owncloud/android/lib/common/http/DebugInterceptorFactory.kt new file mode 100644 index 00000000..713bdca3 --- /dev/null +++ b/owncloudComLibrary/src/release/java/com/owncloud/android/lib/common/http/DebugInterceptorFactory.kt @@ -0,0 +1,29 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2020 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.common.http + +object DebugInterceptorFactory { + fun getInterceptor() = DummyInterceptor() +} \ No newline at end of file diff --git a/sample_client/build.gradle b/sample_client/build.gradle deleted file mode 100644 index dc3d691b..00000000 --- a/sample_client/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -apply plugin: 'com.android.application' - -dependencies { - implementation project(':owncloudComLibrary') -} - -android { - compileSdkVersion 30 - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - - // This is pretty ugly but manifest placeholders don't seem to work very well when using different modules - // See https://github.com/openid/AppAuth-Android/issues/325 - manifestPlaceholders = [appAuthRedirectScheme: ''] - } - - lintOptions { - abortOnError false - } - - packagingOptions { - exclude 'META-INF/LICENSE.txt' - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} diff --git a/sample_client/src/assets/oc_sample.png b/sample_client/src/assets/oc_sample.png deleted file mode 100644 index 31a346cf..00000000 Binary files a/sample_client/src/assets/oc_sample.png and /dev/null differ diff --git a/sample_client/src/assets/oc_sample.txt b/sample_client/src/assets/oc_sample.txt deleted file mode 100644 index 49254be3..00000000 --- a/sample_client/src/assets/oc_sample.txt +++ /dev/null @@ -1 +0,0 @@ -Testing ownCloud uploads diff --git a/sample_client/src/main/AndroidManifest.xml b/sample_client/src/main/AndroidManifest.xml deleted file mode 100644 index ba0c8247..00000000 --- a/sample_client/src/main/AndroidManifest.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file 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 deleted file mode 100644 index 6492b944..00000000 --- a/sample_client/src/main/java/com/owncloud/android/lib/sampleclient/MainActivity.java +++ /dev/null @@ -1,300 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.sampleclient; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.res.AssetManager; -import android.graphics.drawable.BitmapDrawable; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.view.View; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.Toast; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.SingleSessionManager; -import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; -import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; -import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation; -import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation; -import com.owncloud.android.lib.resources.files.RemoteFile; -import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation; -import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation; -import info.hannes.timber.DebugTree; -import timber.log.Timber; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -public class MainActivity extends Activity implements OnRemoteOperationListener, OnDatatransferProgressListener { - - private Handler mHandler; - private OwnCloudClient mClient; - private FilesArrayAdapter mFilesAdapter; - private View mFrame; - - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - Timber.plant(new DebugTree()); - mHandler = new Handler(); - - final Uri serverUri = Uri.parse(getString(R.string.server_base_url)); - - SingleSessionManager.setUserAgent(getUserAgent()); - mClient = OwnCloudClientFactory.createOwnCloudClient(serverUri, this, true); - - mClient.setCredentials( - OwnCloudCredentialsFactory.newBasicCredentials( - getString(R.string.username), - getString(R.string.password) - ) - ); - - mFilesAdapter = new FilesArrayAdapter(this, R.layout.file_in_list); - ((ListView) findViewById(R.id.list_view)).setAdapter(mFilesAdapter); - - // TODO move to background thread or task - AssetManager assets = getAssets(); - try { - String sampleFileName = getString(R.string.sample_file_name); - File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path)); - upFolder.mkdir(); - File upFile = new File(upFolder, sampleFileName); - FileOutputStream fos = new FileOutputStream(upFile); - InputStream is = assets.open(sampleFileName); - int count; - byte[] buffer = new byte[1024]; - while ((count = is.read(buffer, 0, buffer.length)) >= 0) { - fos.write(buffer, 0, count); - } - is.close(); - fos.close(); - } catch (IOException e) { - Toast.makeText(this, R.string.error_copying_sample_file, Toast.LENGTH_SHORT).show(); - Timber.e(e, getString(R.string.error_copying_sample_file)); - } - - mFrame = findViewById(R.id.frame); - } - - @Override - public void onDestroy() { - File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path)); - File upFile = upFolder.listFiles()[0]; - upFile.delete(); - upFolder.delete(); - super.onDestroy(); - } - - public void onClickHandler(View button) { - switch (button.getId()) { - case R.id.button_refresh: - startRefresh(); - break; - case R.id.button_upload: - startUpload(); - break; - case R.id.button_delete_remote: - startRemoteDeletion(); - break; - case R.id.button_download: - startDownload(); - break; - case R.id.button_delete_local: - startLocalDeletion(); - break; - default: - Toast.makeText(this, R.string.youre_doing_it_wrong, Toast.LENGTH_SHORT).show(); - } - } - - private void startRefresh() { - ReadRemoteFolderOperation refreshOperation = new ReadRemoteFolderOperation(File.separator); - refreshOperation.execute(mClient, this, mHandler); - } - - private void startUpload() { - File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path)); - File fileToUpload = upFolder.listFiles()[0]; - String remotePath = File.separator + fileToUpload.getName(); - String mimeType = getString(R.string.sample_file_mimetype); - - // Get the last modification date of the file from the file system - long timeStampLong = fileToUpload.lastModified() / 1000; - String timeStamp = Long.toString(timeStampLong); - - UploadRemoteFileOperation uploadOperation = new UploadRemoteFileOperation(fileToUpload.getAbsolutePath(), - remotePath, mimeType, timeStamp); - uploadOperation.addDatatransferProgressListener(this); - uploadOperation.execute(mClient, this, mHandler); - } - - private void startRemoteDeletion() { - File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path)); - File fileToUpload = upFolder.listFiles()[0]; - String remotePath = File.separator + fileToUpload.getName(); - - RemoveRemoteFileOperation removeOperation = new RemoveRemoteFileOperation(remotePath); - removeOperation.execute(mClient, this, mHandler); - } - - private void startDownload() { - File downFolder = new File(getCacheDir(), getString(R.string.download_folder_path)); - downFolder.mkdir(); - File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path)); - File fileToUpload = upFolder.listFiles()[0]; - String remotePath = File.separator + fileToUpload.getName(); - - DownloadRemoteFileOperation downloadOperation = new DownloadRemoteFileOperation(remotePath, - downFolder.getAbsolutePath()); - downloadOperation.addDatatransferProgressListener(this); - downloadOperation.execute(mClient, this, mHandler); - } - - private void startLocalDeletion() { - File downFolder = new File(getCacheDir(), getString(R.string.download_folder_path)); - File downloadedFile = downFolder.listFiles()[0]; - if (!downloadedFile.delete() && downloadedFile.exists()) { - Toast.makeText(this, R.string.error_deleting_local_file, Toast.LENGTH_SHORT).show(); - } else { - ((TextView) findViewById(R.id.download_progress)).setText("0%"); - findViewById(R.id.frame).setBackgroundDrawable(null); - } - } - - @Override - public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - if (!result.isSuccess()) { - Toast.makeText(this, R.string.todo_operation_finished_in_fail, Toast.LENGTH_SHORT).show(); - Timber.e(result.getException(), result.getLogMessage()); - - } else if (operation instanceof ReadRemoteFolderOperation) { - onSuccessfulRefresh(result); - - } else if (operation instanceof com.owncloud.android.lib.resources.files.UploadRemoteFileOperation) { - onSuccessfulUpload(); - - } else if (operation instanceof RemoveRemoteFileOperation) { - onSuccessfulRemoteDeletion(); - - } else if (operation instanceof DownloadRemoteFileOperation) { - onSuccessfulDownload(); - - } else { - Toast.makeText(this, R.string.todo_operation_finished_in_success, Toast.LENGTH_SHORT).show(); - } - } - - private void onSuccessfulRefresh(RemoteOperationResult result) { - mFilesAdapter.clear(); - List 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 diff --git a/sample_client/src/main/res/drawable-hdpi/ic_launcher.png b/sample_client/src/main/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 6fe153bb..00000000 Binary files a/sample_client/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ diff --git a/sample_client/src/main/res/drawable-ldpi/ic_launcher.png b/sample_client/src/main/res/drawable-ldpi/ic_launcher.png deleted file mode 100644 index 1bc470be..00000000 Binary files a/sample_client/src/main/res/drawable-ldpi/ic_launcher.png and /dev/null differ diff --git a/sample_client/src/main/res/drawable-mdpi/ic_launcher.png b/sample_client/src/main/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 9008b9d3..00000000 Binary files a/sample_client/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ diff --git a/sample_client/src/main/res/layout/file_in_list.xml b/sample_client/src/main/res/layout/file_in_list.xml deleted file mode 100644 index 4c0703a9..00000000 --- a/sample_client/src/main/res/layout/file_in_list.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - diff --git a/sample_client/src/main/res/layout/main.xml b/sample_client/src/main/res/layout/main.xml deleted file mode 100644 index ed7e2e42..00000000 --- a/sample_client/src/main/res/layout/main.xml +++ /dev/null @@ -1,122 +0,0 @@ - - - - - -