1
0
mirror of https://github.com/owncloud/android-library.git synced 2025-06-07 16:06:08 +00:00

Merge pull request #492 from owncloud/master

1.0.15 stable
This commit is contained in:
Abel García de Prada 2022-06-07 18:27:34 +02:00 committed by GitHub
commit 8c2c76c747
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 905 additions and 1390 deletions

View File

@ -6,5 +6,5 @@ jobs:
name: "Validation" name: "Validation"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v1

View File

@ -1,6 +1,6 @@
buildscript { buildscript {
ext { ext {
kotlinVersion = '1.6.10' kotlinVersion = '1.6.21'
moshiVersion = "1.13.0" moshiVersion = "1.13.0"
} }
@ -10,8 +10,8 @@ buildscript {
maven { url "https://plugins.gradle.org/m2/" } maven { url "https://plugins.gradle.org/m2/" }
} }
dependencies { dependencies {
classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.1" classpath "org.jlleitschuh.gradle:ktlint-gradle:10.3.0"
classpath 'com.android.tools.build:gradle:7.0.4' classpath 'com.android.tools.build:gradle:7.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
} }
} }

15
check_code_script.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
check_license_in_file() {
if ! head -n 20 $FILE | grep -q "Permission is hereby granted, free of charge, to any person obtaining a copy"
then
echo "$FILE does not contain a current copyright header"
fi
}
for FILE in $(find owncloudComLibrary/src -name "*.java" -o -name "*.kt")
do
check_license_in_file
done
./gradlew ktlintFormat

View File

@ -12,33 +12,27 @@ dependencies {
implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") { implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") {
exclude module: "kotlin-reflect" exclude module: "kotlin-reflect"
} }
implementation 'org.apache.commons:commons-lang3:3.12.0'
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
testImplementation 'junit:junit:4.13.2' 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 { android {
compileSdkVersion 30 compileSdkVersion 31
defaultConfig { defaultConfig {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 31
versionCode = 10001400
versionName = "1.0.14"
} }
lintOptions { lint {
abortOnError false abortOnError false
ignoreWarnings true ignoreWarnings true
} }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
testOptions { testOptions {
unitTests { unitTests {
includeAndroidResources = true includeAndroidResources = true

View File

@ -19,40 +19,12 @@
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*
*/ */
package com.owncloud.android.lib.common; package com.owncloud.android.lib.common.http
import android.content.Context; import com.facebook.stetho.okhttp3.StethoInterceptor
import android.net.Uri;
import com.owncloud.android.lib.common.http.HttpClient; object DebugInterceptorFactory {
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation; fun getInterceptor() = StethoInterceptor()
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);
}
} }

View File

@ -0,0 +1,215 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common
import android.accounts.AccountManager
import android.accounts.AccountsException
import android.content.Context
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
import com.owncloud.android.lib.resources.status.RemoteServerInfo
import org.apache.commons.lang3.exception.ExceptionUtils
import timber.log.Timber
import java.io.IOException
import java.lang.Exception
/**
* ConnectionValidator
*
* @author Christian Schabesberger
*/
class ConnectionValidator(
val context: Context,
val clearCookiesOnValidation: Boolean
) {
fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager, context: Context): Boolean {
try {
var validationRetryCount = 0
val client = OwnCloudClient(baseClient.baseUri, null, false, singleSessionManager, context)
if (clearCookiesOnValidation) {
client.clearCookies()
} else {
client.cookiesForBaseUri = baseClient.cookiesForBaseUri
}
client.account = baseClient.account
client.credentials = baseClient.credentials
while (validationRetryCount < VALIDATION_RETRY_COUNT) {
Timber.d("validationRetryCout %d", validationRetryCount)
var successCounter = 0
var failCounter = 0
client.setFollowRedirects(true)
if (isOnwCloudStatusOk(client)) {
successCounter++
} else {
failCounter++
}
// Skip the part where we try to check if we can access the parts where we have to be logged in... if we are not logged in
if (baseClient.credentials !is OwnCloudAnonymousCredentials) {
client.setFollowRedirects(false)
val contentReply = canAccessRootFolder(client)
if (contentReply.httpCode == HttpConstants.HTTP_OK) {
if (contentReply.data == true) { //if data is true it means that the content reply was ok
successCounter++
} else {
failCounter++
}
} else {
failCounter++
if (contentReply.httpCode == HttpConstants.HTTP_UNAUTHORIZED) {
checkUnauthorizedAccess(client, singleSessionManager, contentReply.httpCode)
}
}
}
if (successCounter >= failCounter) {
baseClient.credentials = client.credentials
baseClient.cookiesForBaseUri = client.cookiesForBaseUri
return true
}
validationRetryCount++
}
Timber.d("Could not authenticate or get valid data from owncloud")
} catch (e: Exception) {
Timber.d(ExceptionUtils.getStackTrace(e))
}
return false
}
private fun isOnwCloudStatusOk(client: OwnCloudClient): Boolean {
val reply = getOwnCloudStatus(client)
// dont check status code. It currently relais on the broken redirect code of the owncloud client
// TODO: Use okhttp redirect and add this check again
// return reply.httpCode == HttpConstants.HTTP_OK &&
return !reply.isException &&
reply.data != null
}
private fun getOwnCloudStatus(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> {
val remoteStatusOperation = GetRemoteStatusOperation()
return remoteStatusOperation.execute(client)
}
private fun canAccessRootFolder(client: OwnCloudClient): RemoteOperationResult<Boolean> {
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation("/", true)
return checkPathExistenceRemoteOperation.execute(client)
}
/**
* Determines if credentials should be invalidated according the to the HTTPS status
* of a network request just performed.
*
* @param httpStatusCode Result of the last request ran with the 'credentials' belows.
* @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or
* cannot be invalidated with the given arguments.
*/
private fun shouldInvalidateAccountCredentials(credentials: OwnCloudCredentials, account: OwnCloudAccount, httpStatusCode: Int): Boolean {
var shouldInvalidateAccountCredentials = httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED
shouldInvalidateAccountCredentials = shouldInvalidateAccountCredentials and // real credentials
(credentials !is OwnCloudAnonymousCredentials)
// test if have all the needed to effectively invalidate ...
shouldInvalidateAccountCredentials =
shouldInvalidateAccountCredentials and (account.savedAccount != null)
return shouldInvalidateAccountCredentials
}
/**
* Invalidates credentials stored for the given account in the system [AccountManager] and in
* current [SingleSessionManager.getDefaultSingleton] instance.
*
*
* [.shouldInvalidateAccountCredentials] should be called first.
*
*/
private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) {
val am = AccountManager.get(context)
am.invalidateAuthToken(
account.savedAccount.type,
credentials.authToken
)
am.clearPassword(account.savedAccount) // being strict, only needed for Basic Auth credentials
}
/**
* Checks the status code of an execution and decides if should be repeated with fresh credentials.
*
*
* Invalidates current credentials if the request failed as anauthorized.
*
*
* Refresh current credentials if possible, and marks a retry.
*
* @param status
* @param repeatCounter
* @return
*/
private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean {
var credentialsWereRefreshed = false
val account = client.account
val credentials = account.credentials
if (shouldInvalidateAccountCredentials(credentials, account, status)) {
invalidateAccountCredentials(account, credentials)
if (credentials.authTokenCanBeRefreshed()) {
try {
// This command does the actual refresh
account.loadCredentials(context)
// if mAccount.getCredentials().length() == 0 --> refresh failed
client.credentials = account.credentials
credentialsWereRefreshed = true
} catch (e: AccountsException) {
Timber.e(
e, "Error while trying to refresh auth token for %s\ntrace: %s",
account.savedAccount.name,
ExceptionUtils.getStackTrace(e)
)
} catch (e: IOException) {
Timber.e(
e, "Error while trying to refresh auth token for %s\ntrace: %s",
account.savedAccount.name,
ExceptionUtils.getStackTrace(e)
)
}
if (!credentialsWereRefreshed) {
// if credentials are not refreshed, client must be removed
// from the OwnCloudClientManager to prevent it is reused once and again
singleSessionManager.removeClientFor(account)
}
}
// else: onExecute will finish with status 401
}
return credentialsWereRefreshed
}
companion object {
private val VALIDATION_RETRY_COUNT = 3
}
}

View File

@ -25,11 +25,9 @@
package com.owncloud.android.lib.common; package com.owncloud.android.lib.common;
import android.accounts.AccountManager; import android.content.Context;
import android.accounts.AccountsException;
import android.net.Uri; import android.net.Uri;
import at.bitfire.dav4jvm.exception.HttpException;
import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; 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.HttpClient;
import com.owncloud.android.lib.common.http.HttpConstants; import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod; import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import com.owncloud.android.lib.common.network.RedirectionPath;
import com.owncloud.android.lib.common.utils.RandomUtils; import com.owncloud.android.lib.common.utils.RandomUtils;
import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import okhttp3.Cookie; import okhttp3.Cookie;
@ -49,40 +46,54 @@ import java.io.InputStream;
import java.util.List; import java.util.List;
import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER; import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER;
import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID; import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY;
public class OwnCloudClient extends HttpClient { public class OwnCloudClient extends HttpClient {
public static final String WEBDAV_FILES_PATH_4_0 = "/remote.php/dav/files/"; 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"; public static final String STATUS_PATH = "/status.php";
private static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/"; 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_RETRY_COUNT = 2;
private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1;
private static byte[] sExhaustBuffer = new byte[1024];
private static int sIntanceCounter = 0; private static int sIntanceCounter = 0;
private OwnCloudCredentials mCredentials = null; private OwnCloudCredentials mCredentials = null;
private int mInstanceNumber; private int mInstanceNumber;
private Uri mBaseUri; private Uri mBaseUri;
private OwnCloudVersion mVersion = null; private OwnCloudVersion mVersion = null;
private OwnCloudAccount mAccount; 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 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) { if (baseUri == null) {
throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL"); throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL");
} }
mBaseUri = baseUri; mBaseUri = baseUri;
mSynchronizeRequests = synchronizeRequests;
mSingleSessionManager = singleSessionManager;
mInstanceNumber = sIntanceCounter++; mInstanceNumber = sIntanceCounter++;
Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient"); Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient");
clearCredentials(); clearCredentials();
clearCookies(); clearCookies();
mConnectionValidator = connectionValidator;
} }
public void clearCredentials() { public void clearCredentials() {
@ -92,11 +103,27 @@ public class OwnCloudClient extends HttpClient {
} }
public int executeHttpMethod(HttpBaseMethod method) throws Exception { 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 repeatCounter = 0;
int status; int status;
if (mFollowRedirects) {
method.setFollowRedirects(true);
}
boolean retry;
do { do {
repeatCounter++;
retry = false;
String requestId = RandomUtils.generateRandomUUID(); String requestId = RandomUtils.generateRandomUUID();
// Header to allow tracing requests in apache and ownCloud logs // 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.OC_X_REQUEST_ID, requestId);
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent()); method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY); 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()); method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
} }
status = method.execute();
if (mFollowRedirects) { status = method.execute(this);
status = followRedirection(method).getLastStatus();
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); } while (retry && repeatCounter < MAX_RETRY_COUNT);
if (repeatWithFreshCredentials) {
repeatCounter++;
}
} while (repeatWithFreshCredentials);
return status; return status;
} }
private int executeRedirectedHttpMethod(HttpBaseMethod method) throws Exception { private boolean shouldConnectionValidatorBeCalled(HttpBaseMethod method, int status) {
boolean repeatWithFreshCredentials;
int repeatCounter = 0;
int status;
do { return mConnectionValidator != null && (
String requestId = RandomUtils.generateRandomUUID(); (!(mCredentials instanceof OwnCloudAnonymousCredentials) &&
status == HttpConstants.HTTP_UNAUTHORIZED
// Header to allow tracing requests in apache and ownCloud logs ) || (!mFollowRedirects &&
Timber.d("Executing in request with id %s", requestId); !method.getFollowRedirects() &&
method.setRequestHeader(OC_X_REQUEST_ID, requestId); status == HttpConstants.HTTP_MOVED_TEMPORARILY
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;
} }
/** /**
@ -273,26 +229,18 @@ public class OwnCloudClient extends HttpClient {
} }
} }
public String getCookiesString() { public void setCookiesForBaseUri(List<Cookie> cookies) {
StringBuilder cookiesString = new StringBuilder();
List<Cookie> cookieList = getCookiesFromUrl(HttpUrl.parse(mBaseUri.toString()));
if (cookieList != null) {
for (Cookie cookie : cookieList) {
cookiesString.append(cookie.toString()).append(";");
}
}
return cookiesString.toString();
}
public void setCookiesForCurrentAccount(List<Cookie> cookies) {
getOkHttpClient().cookieJar().saveFromResponse( getOkHttpClient().cookieJar().saveFromResponse(
HttpUrl.parse(getAccount().getBaseUri().toString()), HttpUrl.parse(mBaseUri.toString()),
cookies cookies
); );
} }
public List<Cookie> getCookiesForBaseUri() {
return getOkHttpClient().cookieJar().loadForRequest(
HttpUrl.parse(mBaseUri.toString()));
}
public OwnCloudVersion getOwnCloudVersion() { public OwnCloudVersion getOwnCloudVersion() {
return mVersion; return mVersion;
} }
@ -309,95 +257,7 @@ public class OwnCloudClient extends HttpClient {
this.mAccount = account; this.mAccount = account;
} }
/**
* Checks the status code of an execution and decides if should be repeated with fresh credentials.
* <p>
* Invalidates current credentials if the request failed as anauthorized.
* <p>
* Refresh current credentials if possible, and marks a retry.
*
* @param status
* @param repeatCounter
* @return
*/
private boolean checkUnauthorizedAccess(int status, int repeatCounter) {
boolean credentialsWereRefreshed = false;
if (shouldInvalidateAccountCredentials(status)) {
boolean invalidated = invalidateAccountCredentials();
if (invalidated) {
if (getCredentials().authTokenCanBeRefreshed() &&
repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) {
try {
mAccount.loadCredentials(getContext());
// if mAccount.getCredentials().length() == 0 --> refresh failed
setCredentials(mAccount.getCredentials());
credentialsWereRefreshed = true;
} catch (AccountsException | IOException e) {
Timber.e(e, "Error while trying to refresh auth token for %s",
mAccount.getSavedAccount().name
);
}
}
if (!credentialsWereRefreshed && mSingleSessionManager != null) {
// if credentials are not refreshed, client must be removed
// from the OwnCloudClientManager to prevent it is reused once and again
mSingleSessionManager.removeClientFor(mAccount);
}
}
// else: onExecute will finish with status 401
}
return credentialsWereRefreshed;
}
/**
* Determines if credentials should be invalidated according the to the HTTPS status
* of a network request just performed.
*
* @param httpStatusCode Result of the last request ran with the 'credentials' belows.
* @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or
* cannot be invalidated with the given arguments.
*/
private boolean shouldInvalidateAccountCredentials(int httpStatusCode) {
boolean shouldInvalidateAccountCredentials =
(httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED);
shouldInvalidateAccountCredentials &= (mCredentials != null && // real credentials
!(mCredentials instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials));
// test if have all the needed to effectively invalidate ...
shouldInvalidateAccountCredentials &= (mAccount != null && mAccount.getSavedAccount() != null && getContext() != null);
return shouldInvalidateAccountCredentials;
}
/**
* Invalidates credentials stored for the given account in the system {@link AccountManager} and in
* current {@link SingleSessionManager#getDefaultSingleton()} instance.
* <p>
* {@link #shouldInvalidateAccountCredentials(int)} should be called first.
*
* @return 'True' if invalidation was successful, 'false' otherwise.
*/
private boolean invalidateAccountCredentials() {
AccountManager am = AccountManager.get(getContext());
am.invalidateAuthToken(
mAccount.getSavedAccount().type,
mCredentials.getAuthToken()
);
am.clearPassword(mAccount.getSavedAccount()); // being strict, only needed for Basic Auth credentials
return true;
}
public boolean followRedirects() {
return mFollowRedirects;
}
public void setFollowRedirects(boolean followRedirects) { public void setFollowRedirects(boolean followRedirects) {
this.mFollowRedirects = followRedirects; this.mFollowRedirects = followRedirects;
} }
} }

View File

@ -49,6 +49,7 @@ public class SingleSessionManager {
private static SingleSessionManager sDefaultSingleton; private static SingleSessionManager sDefaultSingleton;
private static String sUserAgent; private static String sUserAgent;
private static ConnectionValidator sConnectionValidator;
private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername = new ConcurrentHashMap<>(); private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername = new ConcurrentHashMap<>();
private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername = new ConcurrentHashMap<>(); private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername = new ConcurrentHashMap<>();
@ -60,6 +61,14 @@ public class SingleSessionManager {
return sDefaultSingleton; return sDefaultSingleton;
} }
public static void setConnectionValidator(ConnectionValidator connectionValidator) {
sConnectionValidator = connectionValidator;
}
public static ConnectionValidator getConnectionValidator() {
return sConnectionValidator;
}
public static String getUserAgent() { public static String getUserAgent() {
return sUserAgent; return sUserAgent;
} }
@ -68,7 +77,23 @@ public class SingleSessionManager {
sUserAgent = userAgent; 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 { AuthenticatorException, IOException {
Timber.d("getClientFor starting "); Timber.d("getClientFor starting ");
@ -104,10 +129,11 @@ public class SingleSessionManager {
if (client == null) { if (client == null) {
// no client to reuse - create a new one // no client to reuse - create a new one
client = OwnCloudClientFactory.createOwnCloudClient( client = createOwnCloudClient(
account.getBaseUri(), account.getBaseUri(),
context.getApplicationContext(), context,
true); // TODO remove dependency on OwnCloudClientFactory 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 //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 //injected instance that can be deleted when required
@ -115,7 +141,6 @@ public class SingleSessionManager {
client.clearCredentials(); client.clearCredentials();
client.setAccount(account); client.setAccount(account);
HttpClient.setContext(context);
account.loadCredentials(context); account.loadCredentials(context);
client.setCredentials(account.getCredentials()); client.setCredentials(account.getCredentials());

View File

@ -28,7 +28,7 @@ import okhttp3.CookieJar
import okhttp3.HttpUrl import okhttp3.HttpUrl
class CookieJarImpl( class CookieJarImpl(
private val sCookieStore: HashMap<String, List<Cookie>> private val cookieStore: HashMap<String, List<Cookie>>
) : CookieJar { ) : CookieJar {
fun containsCookieWithName(cookies: List<Cookie>, name: String): Boolean { fun containsCookieWithName(cookies: List<Cookie>, name: String): Boolean {
@ -52,12 +52,11 @@ class CookieJarImpl(
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) { override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
// Avoid duplicated cookies but update // Avoid duplicated cookies but update
val currentCookies: List<Cookie> = sCookieStore[url.host] ?: ArrayList() val currentCookies: List<Cookie> = cookieStore[url.host] ?: ArrayList()
val updatedCookies: List<Cookie> = getUpdatedCookies(currentCookies, cookies) val updatedCookies: List<Cookie> = getUpdatedCookies(currentCookies, cookies)
sCookieStore[url.host] = updatedCookies cookieStore[url.host] = updatedCookies
} }
override fun loadForRequest(url: HttpUrl) = override fun loadForRequest(url: HttpUrl) =
sCookieStore[url.host] ?: ArrayList() cookieStore[url.host] ?: ArrayList()
}
}

View File

@ -21,26 +21,11 @@
* THE SOFTWARE. * THE SOFTWARE.
* *
*/ */
package com.owncloud.android.lib.sampleclient;
import android.content.Context; package com.owncloud.android.lib.common.http
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.owncloud.android.lib.resources.files.RemoteFile; import okhttp3.Interceptor
public class FilesArrayAdapter extends ArrayAdapter<RemoteFile> {
public FilesArrayAdapter(Context context, int resource) {
super(context, resource);
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = (TextView) super.getView(position, convertView, parent);
textView.setText(getItem(position).getRemotePath());
return textView;
}
}
class DummyInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain) = chain.proceed(chain.request())
}

View File

@ -40,7 +40,6 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -54,32 +53,46 @@ import java.util.concurrent.TimeUnit;
*/ */
public class HttpClient { public class HttpClient {
private static OkHttpClient sOkHttpClient; private Context mContext;
private static Context sContext; private HashMap<String, List<Cookie>> mCookieStore = new HashMap<>();
private static HashMap<String, List<Cookie>> sCookieStore = new HashMap<>(); private LogInterceptor mLogInterceptor = new LogInterceptor();
private static LogInterceptor sLogInterceptor;
public static OkHttpClient getOkHttpClient() { private OkHttpClient mOkHttpClient = null;
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);
// TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok. protected HttpClient(Context context) {
//.hostnameVerifier(new BrowserCompatHostnameVerifier()); if (context == null) {
sOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar); Timber.e("Context may not be NULL!");
throw new NullPointerException("Context may not be NULL!");
} catch (Exception e) {
Timber.e(e, "Could not setup SSL system.");
}
} }
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 { try {
return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName()); return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName());
} catch (NoSuchAlgorithmException tlsv13Exception) { } catch (NoSuchAlgorithmException tlsv13Exception) {
@ -100,17 +113,11 @@ public class HttpClient {
} }
} }
private static SSLSocketFactory getNewSslSocketFactory(X509TrustManager trustManager) private OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager,
throws NoSuchAlgorithmException, KeyManagementException { CookieJar cookieJar) {
final SSLContext sslContext = getSslContext();
sslContext.init(null, new TrustManager[]{trustManager}, null);
return sslContext.getSocketFactory();
}
private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager,
CookieJar cookieJar) {
return new OkHttpClient.Builder() return new OkHttpClient.Builder()
.addNetworkInterceptor(getLogInterceptor()) .addNetworkInterceptor(getLogInterceptor())
.addNetworkInterceptor(DebugInterceptorFactory.INSTANCE.getInterceptor())
.protocols(Collections.singletonList(Protocol.HTTP_1_1)) .protocols(Collections.singletonList(Protocol.HTTP_1_1))
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) .readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) .writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
@ -122,26 +129,19 @@ public class HttpClient {
.build(); .build();
} }
public static LogInterceptor getLogInterceptor() {
if (sLogInterceptor == null) {
sLogInterceptor = new LogInterceptor();
}
return sLogInterceptor;
}
public static List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
return sCookieStore.get(httpUrl.host());
}
public Context getContext() { public Context getContext() {
return sContext; return mContext;
} }
public static void setContext(Context context) { public LogInterceptor getLogInterceptor() {
sContext = context; return mLogInterceptor;
}
public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
return mCookieStore.get(httpUrl.host());
} }
public void clearCookies() { public void clearCookies() {
sCookieStore.clear(); mCookieStore.clear();
} }
} }

View File

@ -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 package com.owncloud.android.lib.common.http.methods
import com.owncloud.android.lib.common.http.HttpClient import com.owncloud.android.lib.common.http.HttpClient
@ -14,23 +38,41 @@ import java.net.URL
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
abstract class HttpBaseMethod constructor(url: URL) { abstract class HttpBaseMethod constructor(url: URL) {
var okHttpClient: OkHttpClient
var httpUrl: HttpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException() var httpUrl: HttpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException()
var request: Request var request: Request
var followPermanentRedirects = false
abstract var response: Response abstract var response: Response
var call: Call? = null 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 { init {
okHttpClient = HttpClient.getOkHttpClient()
request = Request.Builder() request = Request.Builder()
.url(httpUrl) .url(httpUrl)
.build() .build()
} }
@Throws(Exception::class) @Throws(Exception::class)
open fun execute(): Int { open fun execute(httpClient: HttpClient): Int {
return onExecute() 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) { open fun setUrl(url: HttpUrl) {
@ -99,6 +141,11 @@ abstract class HttpBaseMethod constructor(url: URL) {
return response.body?.byteStream() return response.body?.byteStream()
} }
/**
* returns the final url after following the last redirect.
*/
open fun getFinalUrl() = response.request.url
/************************* /*************************
*** Connection Params *** *** Connection Params ***
*************************/ *************************/
@ -107,31 +154,19 @@ abstract class HttpBaseMethod constructor(url: URL) {
// Setter // Setter
////////////////////////////// //////////////////////////////
// Connection parameters // Connection parameters
open fun setRetryOnConnectionFailure(retryOnConnectionFailure: Boolean) {
okHttpClient = okHttpClient.newBuilder()
.retryOnConnectionFailure(retryOnConnectionFailure)
.build()
}
open fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) { open fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) {
okHttpClient = okHttpClient.newBuilder() readTimeoutVal = readTimeout
.readTimeout(readTimeout, timeUnit) readTimeoutUnit = timeUnit
.build()
} }
open fun setConnectionTimeout( open fun setConnectionTimeout(
connectionTimeout: Long, connectionTimeout: Long,
timeUnit: TimeUnit timeUnit: TimeUnit
) { ) {
okHttpClient = okHttpClient.newBuilder() connectionTimeoutVal = connectionTimeout
.readTimeout(connectionTimeout, timeUnit) connectionTimeoutUnit = timeUnit
.build()
}
open fun setFollowRedirects(followRedirects: Boolean) {
okHttpClient = okHttpClient.newBuilder()
.followRedirects(followRedirects)
.build()
} }
/************ /************
@ -148,5 +183,5 @@ abstract class HttpBaseMethod constructor(url: URL) {
// For override // For override
////////////////////////////// //////////////////////////////
@Throws(Exception::class) @Throws(Exception::class)
protected abstract fun onExecute(): Int protected abstract fun onExecute(okHttpClient: OkHttpClient): Int
} }

View File

@ -23,6 +23,7 @@
*/ */
package com.owncloud.android.lib.common.http.methods.nonwebdav package com.owncloud.android.lib.common.http.methods.nonwebdav
import okhttp3.OkHttpClient
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
@ -33,10 +34,10 @@ import java.net.URL
*/ */
class DeleteMethod(url: URL) : HttpMethod(url) { class DeleteMethod(url: URL) : HttpMethod(url) {
@Throws(IOException::class) @Throws(IOException::class)
override fun onExecute(): Int { override fun onExecute(okHttpClient: OkHttpClient): Int {
request = request.newBuilder() request = request.newBuilder()
.delete() .delete()
.build() .build()
return super.onExecute() return super.onExecute(okHttpClient)
} }
} }

View File

@ -23,6 +23,7 @@
*/ */
package com.owncloud.android.lib.common.http.methods.nonwebdav package com.owncloud.android.lib.common.http.methods.nonwebdav
import okhttp3.OkHttpClient
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
@ -33,10 +34,10 @@ import java.net.URL
*/ */
class GetMethod(url: URL) : HttpMethod(url) { class GetMethod(url: URL) : HttpMethod(url) {
@Throws(IOException::class) @Throws(IOException::class)
override fun onExecute(): Int { override fun onExecute(okHttpClient: OkHttpClient): Int {
request = request.newBuilder() request = request.newBuilder()
.get() .get()
.build() .build()
return super.onExecute() return super.onExecute(okHttpClient)
} }
} }

View File

@ -24,6 +24,7 @@
package com.owncloud.android.lib.common.http.methods.nonwebdav package com.owncloud.android.lib.common.http.methods.nonwebdav
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
import okhttp3.OkHttpClient
import okhttp3.Response import okhttp3.Response
import java.net.URL import java.net.URL
@ -38,7 +39,7 @@ abstract class HttpMethod(
override lateinit var response: Response override lateinit var response: Response
public override fun onExecute(): Int { public override fun onExecute(okHttpClient: OkHttpClient): Int {
call = okHttpClient.newCall(request) call = okHttpClient.newCall(request)
call?.let { response = it.execute() } call?.let { response = it.execute() }
return super.statusCode return super.statusCode

View File

@ -23,6 +23,7 @@
*/ */
package com.owncloud.android.lib.common.http.methods.nonwebdav package com.owncloud.android.lib.common.http.methods.nonwebdav
import okhttp3.OkHttpClient
import okhttp3.RequestBody import okhttp3.RequestBody
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
@ -37,10 +38,10 @@ class PostMethod(
private val postRequestBody: RequestBody private val postRequestBody: RequestBody
) : HttpMethod(url) { ) : HttpMethod(url) {
@Throws(IOException::class) @Throws(IOException::class)
override fun onExecute(): Int { override fun onExecute(okHttpClient: OkHttpClient): Int {
request = request.newBuilder() request = request.newBuilder()
.post(postRequestBody) .post(postRequestBody)
.build() .build()
return super.onExecute() return super.onExecute(okHttpClient)
} }
} }

View File

@ -23,6 +23,7 @@
*/ */
package com.owncloud.android.lib.common.http.methods.nonwebdav package com.owncloud.android.lib.common.http.methods.nonwebdav
import okhttp3.OkHttpClient
import okhttp3.RequestBody import okhttp3.RequestBody
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
@ -37,10 +38,10 @@ class PutMethod(
private val putRequestBody: RequestBody private val putRequestBody: RequestBody
) : HttpMethod(url) { ) : HttpMethod(url) {
@Throws(IOException::class) @Throws(IOException::class)
override fun onExecute(): Int { override fun onExecute(okHttpClient: OkHttpClient): Int {
request = request.newBuilder() request = request.newBuilder()
.put(putRequestBody) .put(putRequestBody)
.build() .build()
return super.onExecute() return super.onExecute(okHttpClient)
} }
} }

View File

@ -23,6 +23,7 @@
*/ */
package com.owncloud.android.lib.common.http.methods.webdav package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.DavOCResource
import okhttp3.Response import okhttp3.Response
import java.net.URL import java.net.URL
@ -38,7 +39,7 @@ class CopyMethod(
private val forceOverride: Boolean private val forceOverride: Boolean
) : DavMethod(url) { ) : DavMethod(url) {
@Throws(Exception::class) @Throws(Exception::class)
public override fun onExecute(): Int { public override fun onDavExecute(davResource: DavOCResource): Int {
davResource.copy( davResource.copy(
destinationUrl, destinationUrl,
forceOverride, forceOverride,

View File

@ -29,14 +29,11 @@ import at.bitfire.dav4jvm.exception.HttpException
import at.bitfire.dav4jvm.exception.RedirectException import at.bitfire.dav4jvm.exception.RedirectException
import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
import okhttp3.HttpUrl import okhttp3.OkHttpClient
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Protocol import okhttp3.Protocol
import okhttp3.Response import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody import okhttp3.ResponseBody.Companion.toResponseBody
import java.net.MalformedURLException
import java.net.URL import java.net.URL
import java.util.concurrent.TimeUnit
/** /**
* Wrapper to perform WebDAV (dav4android) calls * Wrapper to perform WebDAV (dav4android) calls
@ -44,27 +41,25 @@ import java.util.concurrent.TimeUnit
* @author David González Verdugo * @author David González Verdugo
*/ */
abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) { abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) {
protected var davResource: DavOCResource
override lateinit var response: Response override lateinit var response: Response
private var davResource: DavOCResource? = null
init {
val httpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException()
davResource = DavOCResource(
okHttpClient,
httpUrl,
log
)
}
override fun abort() { override fun abort() {
davResource.cancelCall() davResource?.cancelCall()
} }
protected abstract fun onDavExecute(davResource: DavOCResource): Int
@Throws(Exception::class) @Throws(Exception::class)
override fun execute(): Int { override fun onExecute(okHttpClient: OkHttpClient): Int {
return try { return try {
onExecute() davResource = DavOCResource(
okHttpClient.newBuilder().followRedirects(false).build(),
httpUrl,
log
)
onDavExecute(davResource!!)
} catch (httpException: HttpException) { } catch (httpException: HttpException) {
// Modify responses with information gathered from exceptions // Modify responses with information gathered from exceptions
if (httpException is RedirectException) { 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 // Getter
////////////////////////////// //////////////////////////////
override fun setRetryOnConnectionFailure(retryOnConnectionFailure: Boolean) {
super.setRetryOnConnectionFailure(retryOnConnectionFailure)
davResource = DavOCResource(
okHttpClient,
request.url,
log
)
}
override val isAborted: Boolean override val isAborted: Boolean
get() = davResource.isCallAborted() get() = davResource?.isCallAborted() ?: false
} }

View File

@ -23,6 +23,7 @@
*/ */
package com.owncloud.android.lib.common.http.methods.webdav package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.DavOCResource
import okhttp3.Response import okhttp3.Response
import java.net.URL import java.net.URL
@ -34,7 +35,7 @@ import java.net.URL
*/ */
class MkColMethod(url: URL) : DavMethod(url) { class MkColMethod(url: URL) : DavMethod(url) {
@Throws(Exception::class) @Throws(Exception::class)
public override fun onExecute(): Int { public override fun onDavExecute(davResource: DavOCResource): Int {
davResource.mkCol( davResource.mkCol(
xmlBody = null, xmlBody = null,
listOfHeaders = super.getRequestHeadersAsHashMap() listOfHeaders = super.getRequestHeadersAsHashMap()

View File

@ -23,6 +23,7 @@
*/ */
package com.owncloud.android.lib.common.http.methods.webdav package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.DavOCResource
import okhttp3.Response import okhttp3.Response
import java.net.URL import java.net.URL
@ -38,7 +39,7 @@ class MoveMethod(
private val forceOverride: Boolean private val forceOverride: Boolean
) : DavMethod(url) { ) : DavMethod(url) {
@Throws(Exception::class) @Throws(Exception::class)
public override fun onExecute(): Int { override fun onDavExecute(davResource: DavOCResource): Int {
davResource.move( davResource.move(
destinationUrl, destinationUrl,
forceOverride, forceOverride,

View File

@ -23,6 +23,7 @@
*/ */
package com.owncloud.android.lib.common.http.methods.webdav package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.DavOCResource
import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.Response.HrefRelation import at.bitfire.dav4jvm.Response.HrefRelation
@ -47,10 +48,10 @@ class PropfindMethod(
private set private set
@Throws(IOException::class, DavException::class) @Throws(IOException::class, DavException::class)
public override fun onExecute(): Int { public override fun onDavExecute(davResource: DavOCResource): Int {
davResource.propfind( davResource.propfind(
depth = depth, depth = depth,
reqProp = *propertiesToRequest, reqProp = propertiesToRequest,
listOfHeaders = super.getRequestHeadersAsHashMap(), listOfHeaders = super.getRequestHeadersAsHashMap(),
callback = { response: Response, hrefRelation: HrefRelation? -> callback = { response: Response, hrefRelation: HrefRelation? ->
when (hrefRelation) { when (hrefRelation) {

View File

@ -23,6 +23,7 @@
*/ */
package com.owncloud.android.lib.common.http.methods.webdav package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.DavOCResource
import at.bitfire.dav4jvm.exception.HttpException import at.bitfire.dav4jvm.exception.HttpException
import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.HttpConstants
import okhttp3.RequestBody import okhttp3.RequestBody
@ -39,7 +40,7 @@ class PutMethod(
private val putRequestBody: RequestBody private val putRequestBody: RequestBody
) : DavMethod(url) { ) : DavMethod(url) {
@Throws(IOException::class, HttpException::class) @Throws(IOException::class, HttpException::class)
public override fun onExecute(): Int { public override fun onDavExecute(davResource: DavOCResource): Int {
davResource.put( davResource.put(
putRequestBody, putRequestBody,
super.getRequestHeader(HttpConstants.IF_MATCH_HEADER), super.getRequestHeader(HttpConstants.IF_MATCH_HEADER),

View File

@ -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; package com.owncloud.android.lib.common.operations;
import android.accounts.Account; import android.accounts.Account;
@ -135,7 +159,7 @@ public abstract class RemoteOperation<T> implements Runnable {
if (mAccount != null && mContext != null) { if (mAccount != null && mContext != null) {
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
mClient = SingleSessionManager.getDefaultSingleton(). mClient = SingleSessionManager.getDefaultSingleton().
getClientFor(ocAccount, mContext); getClientFor(ocAccount, mContext, SingleSessionManager.getConnectionValidator());
} else { } else {
throw new IllegalStateException("Trying to run a remote operation " + throw new IllegalStateException("Trying to run a remote operation " +
"asynchronously with no client and no chance to create one (no account)"); "asynchronously with no client and no chance to create one (no account)");
@ -265,4 +289,4 @@ public abstract class RemoteOperation<T> implements Runnable {
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend); mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license /* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH. * Copyright (C) 2022 ownCloud GmbH.
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -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.http.methods.HttpBaseMethod;
import com.owncloud.android.lib.common.network.CertificateCombinedException; import com.owncloud.android.lib.common.network.CertificateCombinedException;
import okhttp3.Headers; import okhttp3.Headers;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.json.JSONException; import org.json.JSONException;
import timber.log.Timber; import timber.log.Timber;
@ -59,13 +60,15 @@ public class RemoteOperationResult<T>
* Generated - should be refreshed every time the class changes!! * Generated - should be refreshed every time the class changes!!
*/ */
private static final long serialVersionUID = 4968939884332372230L; 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 boolean mSuccess = false;
private int mHttpCode = -1; private int mHttpCode = -1;
private String mHttpPhrase = null; private String mHttpPhrase = null;
private Exception mException = null; private Exception mException = null;
private ResultCode mCode = ResultCode.UNKNOWN_ERROR; private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
private String mRedirectedLocation; private String mRedirectedLocation = "";
private List<String> mAuthenticate = new ArrayList<>(); private List<String> mAuthenticate = new ArrayList<>();
private String mLastPermanentLocation = null; private String mLastPermanentLocation = null;
private T mData = null; private T mData = null;
@ -112,6 +115,14 @@ public class RemoteOperationResult<T>
*/ */
public RemoteOperationResult(Exception e) { public RemoteOperationResult(Exception e) {
mException = 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) { if (e instanceof OperationCancelledException) {
mCode = ResultCode.CANCELLED; mCode = ResultCode.CANCELLED;
@ -248,11 +259,11 @@ public class RemoteOperationResult<T>
this(httpCode, httpPhrase); this(httpCode, httpPhrase);
if (headers != null) { if (headers != null) {
for (Map.Entry<String, List<String>> header : headers.toMultimap().entrySet()) { for (Map.Entry<String, List<String>> header : headers.toMultimap().entrySet()) {
if ("location".equals(header.getKey().toLowerCase())) { if (LOCATION.equalsIgnoreCase(header.getKey())) {
mRedirectedLocation = header.getValue().get(0); mRedirectedLocation = header.getValue().get(0);
continue; continue;
} }
if ("www-authenticate".equals(header.getKey().toLowerCase())) { if (WWW_AUTHENTICATE.equalsIgnoreCase(header.getKey())) {
for (String value: header.getValue()) { for (String value: header.getValue()) {
mAuthenticate.add(value.toLowerCase()); mAuthenticate.add(value.toLowerCase());
} }
@ -321,7 +332,7 @@ public class RemoteOperationResult<T>
mHttpPhrase = errorMessage; mHttpPhrase = errorMessage;
} }
} catch (Exception e) { } 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) // mCode stays as set in this(success, httpCode, headers)
} }
} }

View File

@ -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 package com.owncloud.android.lib.common.utils
import info.hannes.timber.FileLoggingTree import info.hannes.timber.FileLoggingTree

View File

@ -1,21 +1,27 @@
/** /* ownCloud Android Library is available under MIT license
* ownCloud Android client application * 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 * The above copyright notice and this permission notice shall be included in
* it under the terms of the GNU General Public License version 2, * all copies or substantial portions of the Software.
* as published by the Free Software Foundation. *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.owncloud.android.lib.resources package com.owncloud.android.lib.resources

View File

@ -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.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils.allPropset import com.owncloud.android.lib.common.http.methods.webdav.DavUtils.allPropset
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
import com.owncloud.android.lib.common.network.RedirectionPath
import com.owncloud.android.lib.common.network.WebdavUtils import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult
@ -44,26 +43,18 @@ import java.util.concurrent.TimeUnit
* @author Abel García de Prada * @author Abel García de Prada
* *
* @param remotePath Path to append to the URL owned by the client instance. * @param remotePath Path to append to the URL owned by the client instance.
* @param 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 * needed to check user credentials
*/ */
class CheckPathExistenceRemoteOperation( class CheckPathExistenceRemoteOperation(
val remotePath: String? = "", val remotePath: String? = "",
val isUserLogged: Boolean val isUserLoggedIn: Boolean
) : RemoteOperation<Boolean>() { ) : RemoteOperation<Boolean>() {
/**
* Gets the sequence of redirections followed during the execution of the operation.
*
* @return Sequence of redirections followed, if any, or NULL if the operation was not executed.
*/
var redirectionPath: RedirectionPath? = null
private set
override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> { override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> {
val previousFollowRedirects = client.followRedirects()
return try { return try {
val stringUrl = val stringUrl =
if (isUserLogged) client.baseFilesWebDavUri.toString() if (isUserLoggedIn) client.baseFilesWebDavUri.toString()
else client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath) else client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)
val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropset).apply { val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropset).apply {
@ -71,12 +62,7 @@ class CheckPathExistenceRemoteOperation(
setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
} }
client.setFollowRedirects(false)
var status = client.executeHttpMethod(propFindMethod) var status = client.executeHttpMethod(propFindMethod)
if (previousFollowRedirects) {
redirectionPath = client.followRedirection(propFindMethod)
status = redirectionPath?.lastStatus!!
}
/* PROPFIND method /* PROPFIND method
* 404 NOT FOUND: path doesn't exist, * 404 NOT FOUND: path doesn't exist,
* 207 MULTI_STATUS: path exists. * 207 MULTI_STATUS: path exists.
@ -94,16 +80,9 @@ class CheckPathExistenceRemoteOperation(
"Existence check for ${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)} : ${result.logMessage}" "Existence check for ${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)} : ${result.logMessage}"
) )
result 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 private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
companion object { companion object {

View File

@ -92,7 +92,8 @@ public class CopyRemoteFileOperation extends RemoteOperation<String> {
RemoteOperationResult result; RemoteOperationResult result;
try { try {
CopyMethod copyMethod = 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), client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath),
mOverwrite); mOverwrite);

View File

@ -87,7 +87,8 @@ public class CreateRemoteFolderOperation extends RemoteOperation {
RemoteOperationResult result; RemoteOperationResult result;
try { try {
Uri webDavUri = createChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri(); 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.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
mkcol.setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS); mkcol.setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
final int status = client.executeHttpMethod(mkcol); final int status = client.executeHttpMethod(mkcol);

View File

@ -0,0 +1,77 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import timber.log.Timber
import java.net.URL
import java.util.concurrent.TimeUnit
/**
* Operation to get the base url, which might differ in case of a redirect.
*
* @author Christian Schabesberger
*/
class GetBaseUrlRemoteOperation : RemoteOperation<String?>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<String?> {
return try {
val stringUrl = client.baseFilesWebDavUri.toString()
val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropset).apply {
setReadTimeout(TIMEOUT, TimeUnit.SECONDS)
setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS)
}
val status = client.executeHttpMethod(propFindMethod)
if (isSuccess(status)) {
RemoteOperationResult<String?>(RemoteOperationResult.ResultCode.OK).apply {
data = propFindMethod.getFinalUrl().toString()
}
} else {
RemoteOperationResult<String?>(propFindMethod).apply {
data = null
}
}
} catch (e: Exception) {
Timber.e(e, "Could not get actuall (or redirected) base URL from base url (/).")
RemoteOperationResult<String?>(e)
}
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
companion object {
/**
* Maximum time to wait for a response from the server in milliseconds.
*/
private const val TIMEOUT = 10_000L
}
}

View File

@ -76,7 +76,8 @@ public class ReadRemoteFileOperation extends RemoteOperation<RemoteFile> {
/// take the duty of check the server for the current state of the file there /// take the duty of check the server for the current state of the file there
try { try {
// remote request // remote request
propfind = new PropfindMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), propfind = new PropfindMethod(
new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
DEPTH_0, DEPTH_0,
DavUtils.getAllPropset()); DavUtils.getAllPropset());

View File

@ -77,8 +77,6 @@ public class ReadRemoteFolderOperation extends RemoteOperation<ArrayList<RemoteF
DavConstants.DEPTH_1, DavConstants.DEPTH_1,
DavUtils.getAllPropset()); DavUtils.getAllPropset());
client.setFollowRedirects(true);
int status = client.executeHttpMethod(propfindMethod); int status = client.executeHttpMethod(propfindMethod);
if (isSuccess(status)) { if (isSuccess(status)) {

View File

@ -91,7 +91,8 @@ public class RenameRemoteFileOperation extends RemoteOperation {
return new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE); return new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
} }
final MoveMethod move = new MoveMethod(new URL(client.getUserFilesWebDavUri() + final MoveMethod move = new MoveMethod(
new URL(client.getUserFilesWebDavUri() +
WebdavUtils.encodePath(mOldRemotePath)), WebdavUtils.encodePath(mOldRemotePath)),
client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mNewRemotePath), false); client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mNewRemotePath), false);

View File

@ -50,7 +50,7 @@ class UploadFileFromContentUriOperation(
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> { override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
val putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(uploadPath)), requestBody).apply { 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_TOTAL_LENGTH_HEADER, requestBody.contentLength().toString())
addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, lastModified) addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, lastModified)
} }

View File

@ -92,7 +92,8 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation
result = new RemoteOperationResult<>(new OperationCancelledException()); result = new RemoteOperationResult<>(new OperationCancelledException());
break; break;
} else { } 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) { if (chunkIndex == chunkCount - 1) {
// Added a high timeout to the last chunk due to when the last chunk // Added a high timeout to the last chunk due to when the last chunk

View File

@ -1,22 +1,26 @@
/** /* ownCloud Android Library is available under MIT license
* ownCloud Android client application * Copyright (C) 2022 ownCloud GmbH.
* *
* @author Abel García de Prada * Permission is hereby granted, free of charge, to any person obtaining a copy
* Copyright (C) 2020 ownCloud GmbH. * 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 * The above copyright notice and this permission notice shall be included in
* it under the terms of the GNU General Public License version 2, * all copies or substantial portions of the Software.
* as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* GNU General Public License for more details. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.owncloud.android.lib.resources.files.services.implementation package com.owncloud.android.lib.resources.files.services.implementation
import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.OwnCloudClient
@ -29,6 +33,6 @@ class OCFileService(override val client: OwnCloudClient) :
override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean> = override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean> =
CheckPathExistenceRemoteOperation( CheckPathExistenceRemoteOperation(
remotePath = path, remotePath = path,
isUserLogged = isUserLogged isUserLoggedIn = isUserLogged
).execute(client) ).execute(client)
} }

View File

@ -55,6 +55,7 @@ class GetOIDCDiscoveryRemoteOperation : RemoteOperation<OIDCDiscoveryResponse>()
addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
} }
getMethod.followRedirects = true
val status = client.executeHttpMethod(getMethod) val status = client.executeHttpMethod(getMethod)
val responseBody = getMethod.getResponseBodyAsString() val responseBody = getMethod.getResponseBodyAsString()

View File

@ -138,7 +138,6 @@ class CreateRemoteShareOperation(
} }
private fun createFormBody(): FormBody { private fun createFormBody(): FormBody {
val formBodyBuilder = FormBody.Builder() val formBodyBuilder = FormBody.Builder()
.add(PARAM_PATH, remoteFilePath) .add(PARAM_PATH, remoteFilePath)
.add(PARAM_SHARE_TYPE, shareType.value.toString()) .add(PARAM_SHARE_TYPE, shareType.value.toString())

View File

@ -104,6 +104,7 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> { override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> {
val requestUri = buildRequestUri(client.baseUri) val requestUri = buildRequestUri(client.baseUri)
val deleteMethod = DeleteMethod(URL(requestUri.toString())).apply { val deleteMethod = DeleteMethod(URL(requestUri.toString())).apply {

View File

@ -195,6 +195,7 @@ class UpdateRemoteShareOperation
return formBodyBuilder return formBodyBuilder
} }
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> { override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> {
val requestUri = buildRequestUri(client.baseUri) val requestUri = buildRequestUri(client.baseUri)
@ -205,7 +206,6 @@ class UpdateRemoteShareOperation
addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
} }
return try { return try {
val status = client.executeHttpMethod(putMethod) val status = client.executeHttpMethod(putMethod)
val response = putMethod.getResponseBodyAsString() val response = putMethod.getResponseBodyAsString()

View File

@ -1,21 +1,27 @@
/** /* ownCloud Android Library is available under MIT license
* ownCloud Android client application * 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 * The above copyright notice and this permission notice shall be included in
* it under the terms of the GNU General Public License version 2, * all copies or substantial portions of the Software.
* as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* GNU General Public License for more details. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.owncloud.android.lib.resources.shares.services package com.owncloud.android.lib.resources.shares.services

View File

@ -1,22 +1,28 @@
/** /* ownCloud Android Library is available under MIT license
* ownCloud Android client application * Copyright (C) 2022 ownCloud GmbH.
* *
* @author Christian Schabesberger * @author Christian Schabesberger
* @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 * The above copyright notice and this permission notice shall be included in
* it under the terms of the GNU General Public License version 2, * all copies or substantial portions of the Software.
* as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* GNU General Public License for more details. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.owncloud.android.lib.resources.shares.services package com.owncloud.android.lib.resources.shares.services

View File

@ -1,21 +1,27 @@
/** /* ownCloud Android Library is available under MIT license
* ownCloud Android client application * 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 * The above copyright notice and this permission notice shall be included in
* it under the terms of the GNU General Public License version 2, * all copies or substantial portions of the Software.
* as published by the Free Software Foundation. *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.owncloud.android.lib.resources.shares.services.implementation package com.owncloud.android.lib.resources.shares.services.implementation

View File

@ -1,21 +1,26 @@
/** /* ownCloud Android Library is available under MIT license
* ownCloud Android client application * 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 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* it under the terms of the GNU General Public License version 2, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* as published by the Free Software Foundation. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.owncloud.android.lib.resources.shares.services.implementation package com.owncloud.android.lib.resources.shares.services.implementation

View File

@ -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.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.lib.resources.status.HttpScheme.HTTPS_PREFIX 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_PREFIX
import com.owncloud.android.lib.resources.status.HttpScheme.HTTP_SCHEME
import org.json.JSONException import org.json.JSONException
import timber.log.Timber
/** /**
* Checks if the server is valid * Checks if the server is valid
@ -45,27 +43,25 @@ import timber.log.Timber
class GetRemoteStatusOperation : RemoteOperation<RemoteServerInfo>() { class GetRemoteStatusOperation : RemoteOperation<RemoteServerInfo>() {
public override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> { public override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> {
client.baseUri = buildFullHttpsUrl(client.baseUri) if (!usesHttpOrHttps(client.baseUri)) {
client.baseUri = buildFullHttpsUrl(client.baseUri)
var result = tryToConnect(client)
if (!(result.code == ResultCode.OK || result.code == ResultCode.OK_SSL) && !result.isSslRecoverableException) {
Timber.d("Establishing secure connection failed, trying non secure connection")
client.baseUri = client.baseUri.buildUpon().scheme(HTTP_SCHEME).build()
result = tryToConnect(client)
} }
return tryToConnect(client)
return result
} }
private fun updateClientBaseUrl(client: OwnCloudClient, newBaseUrl: String) {
client.baseUri = Uri.parse(newBaseUrl)
}
private fun tryToConnect(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> { private fun tryToConnect(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> {
val baseUrl = client.baseUri.toString() val baseUrl = client.baseUri.toString()
client.setFollowRedirects(false)
return try { return try {
val requester = StatusRequester() val requester = StatusRequester()
val requestResult = requester.requestAndFollowRedirects(baseUrl, client) val requestResult = requester.request(baseUrl, client)
requester.handleRequestResult(requestResult, baseUrl).also { val result = requester.handleRequestResult(requestResult, baseUrl)
client.baseUri = Uri.parse(it.data.baseUrl) updateClientBaseUrl(client, result.data.baseUrl)
} return result
} catch (e: JSONException) { } catch (e: JSONException) {
RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED) RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED)
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -47,7 +47,7 @@ internal class StatusRequester {
redirectedUrl: String redirectedUrl: String
) = redirectedToNonSecureLocationBefore || ) = redirectedToNonSecureLocationBefore ||
(baseUrl.startsWith(HTTPS_SCHEME) && (baseUrl.startsWith(HTTPS_SCHEME) &&
!redirectedUrl.startsWith(HTTPS_SCHEME)) !redirectedUrl.startsWith(HTTPS_SCHEME))
fun updateLocationWithRedirectPath(oldLocation: String, redirectedLocation: String): String { fun updateLocationWithRedirectPath(oldLocation: String, redirectedLocation: String): String {
/** Redirection with different endpoint. /** Redirection with different endpoint.
@ -73,36 +73,18 @@ internal class StatusRequester {
data class RequestResult( data class RequestResult(
val getMethod: GetMethod, val getMethod: GetMethod,
val status: Int, val status: Int,
val redirectedToUnsecureLocation: Boolean,
val lastLocation: String val lastLocation: String
) )
fun requestAndFollowRedirects(baseLocation: String, client: OwnCloudClient): RequestResult { fun request(baseLocation: String, client: OwnCloudClient): RequestResult {
var currentLocation = baseLocation + OwnCloudClient.STATUS_PATH val currentLocation = baseLocation + OwnCloudClient.STATUS_PATH
var redirectedToUnsecureLocation = false
var status: Int var status: Int
val getMethod = getGetMethod(currentLocation)
while (true) { getMethod.followPermanentRedirects = true
val getMethod = getGetMethod(currentLocation) status = client.executeHttpMethod(getMethod)
status = client.executeHttpMethod(getMethod) return RequestResult(getMethod, status, getMethod.getFinalUrl().toString())
val result =
if (status.isSuccess()) RemoteOperationResult<OwnCloudVersion>(RemoteOperationResult.ResultCode.OK)
else RemoteOperationResult(getMethod)
if (result.redirectedLocation.isNullOrEmpty() || result.isSuccess) {
return RequestResult(getMethod, status, redirectedToUnsecureLocation, currentLocation)
} else {
val nextLocation = updateLocationWithRedirectPath(currentLocation, result.redirectedLocation)
redirectedToUnsecureLocation =
isRedirectedToNonSecureConnection(
redirectedToUnsecureLocation,
currentLocation,
nextLocation
)
currentLocation = nextLocation
}
}
} }
private fun Int.isSuccess() = this == HttpConstants.HTTP_OK 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; // 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) // every app will decide how to act if (ocVersion.isVersionValid() == false)
val result: RemoteOperationResult<RemoteServerInfo> = val result: RemoteOperationResult<RemoteServerInfo> =
if (requestResult.redirectedToUnsecureLocation) { if (baseUrl.startsWith(HTTPS_SCHEME)) RemoteOperationResult(RemoteOperationResult.ResultCode.OK_SSL)
RemoteOperationResult(RemoteOperationResult.ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION) else RemoteOperationResult(RemoteOperationResult.ResultCode.OK_NO_SSL)
} else {
if (baseUrl.startsWith(HTTPS_SCHEME)) RemoteOperationResult(RemoteOperationResult.ResultCode.OK_SSL)
else RemoteOperationResult(RemoteOperationResult.ResultCode.OK_NO_SSL)
}
val finalUrl = URL(requestResult.lastLocation) val finalUrl = URL(requestResult.lastLocation)
val finalBaseUrl = URL( val finalBaseUrl = URL(
finalUrl.protocol, finalUrl.protocol,

View File

@ -1,21 +1,27 @@
/** /* ownCloud Android Library is available under MIT license
* ownCloud Android client application * 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 * The above copyright notice and this permission notice shall be included in
* it under the terms of the GNU General Public License version 2, * all copies or substantial portions of the Software.
* as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* GNU General Public License for more details. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.owncloud.android.lib.resources.status.services package com.owncloud.android.lib.resources.status.services

View File

@ -23,12 +23,13 @@
*/ */
package com.owncloud.android.lib.resources.status.services package com.owncloud.android.lib.resources.status.services
import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.status.RemoteServerInfo import com.owncloud.android.lib.resources.status.RemoteServerInfo
interface ServerInfoService { interface ServerInfoService {
fun checkPathExistence(path: String, isUserLogged: Boolean, client: OwnCloudClient): RemoteOperationResult<Boolean> fun checkPathExistence(path: String, isUserLoggedIn: Boolean, client: OwnCloudClient): RemoteOperationResult<Boolean>
fun getRemoteStatus(path: String, client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> fun getRemoteStatus(path: String, client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo>
} }

View File

@ -1,21 +1,27 @@
/** /* ownCloud Android Library is available under MIT license
* ownCloud Android client application * 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 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* it under the terms of the GNU General Public License version 2, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* as published by the Free Software Foundation. * 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 <http://www.gnu.org/licenses/>.
*/ */
package com.owncloud.android.lib.resources.status.services.implementation package com.owncloud.android.lib.resources.status.services.implementation

View File

@ -1,24 +1,32 @@
/** /* ownCloud Android Library is available under MIT license
* ownCloud Android client application * Copyright (C) 2022 ownCloud GmbH.
* *
* @author Abel García de Prada * @author Abel García de Prada
* Copyright (C) 2020 ownCloud GmbH.
* *
* This program is free software: you can redistribute it and/or modify * Permission is hereby granted, free of charge, to any person obtaining a copy
* it under the terms of the GNU General Public License version 2, * of this software and associated documentation files (the "Software"), to deal
* as published by the Free Software Foundation. * 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, * The above copyright notice and this permission notice shall be included in
* but WITHOUT ANY WARRANTY; without even the implied warranty of * all copies or substantial portions of the Software.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.owncloud.android.lib.resources.status.services.implementation package com.owncloud.android.lib.resources.status.services.implementation
import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
@ -30,12 +38,12 @@ class OCServerInfoService : ServerInfoService {
override fun checkPathExistence( override fun checkPathExistence(
path: String, path: String,
isUserLogged: Boolean, isUserLoggedIn: Boolean,
client: OwnCloudClient client: OwnCloudClient
): RemoteOperationResult<Boolean> = ): RemoteOperationResult<Boolean> =
CheckPathExistenceRemoteOperation( CheckPathExistenceRemoteOperation(
remotePath = path, remotePath = path,
isUserLogged = true isUserLoggedIn = true
).execute(client) ).execute(client)
override fun getRemoteStatus( override fun getRemoteStatus(

View File

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

View File

@ -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()
}

View File

@ -1,31 +0,0 @@
apply plugin: 'com.android.application'
dependencies {
implementation project(':owncloudComLibrary')
}
android {
compileSdkVersion 30
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
// This is pretty ugly but manifest placeholders don't seem to work very well when using different modules
// See https://github.com/openid/AppAuth-Android/issues/325
manifestPlaceholders = [appAuthRedirectScheme: '']
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

View File

@ -1 +0,0 @@
Testing ownCloud uploads

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2016 ownCloud GmbH.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<manifest package="com.owncloud.android.lib.sampleclient"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<activity
android:name="MainActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:screenOrientation="portrait"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,300 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.sampleclient;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientFactory;
import com.owncloud.android.lib.common.SingleSessionManager;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation;
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
import com.owncloud.android.lib.resources.files.RemoteFile;
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation;
import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
import info.hannes.timber.DebugTree;
import timber.log.Timber;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends Activity implements OnRemoteOperationListener, OnDatatransferProgressListener {
private Handler mHandler;
private OwnCloudClient mClient;
private FilesArrayAdapter mFilesAdapter;
private View mFrame;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Timber.plant(new DebugTree());
mHandler = new Handler();
final Uri serverUri = Uri.parse(getString(R.string.server_base_url));
SingleSessionManager.setUserAgent(getUserAgent());
mClient = OwnCloudClientFactory.createOwnCloudClient(serverUri, this, true);
mClient.setCredentials(
OwnCloudCredentialsFactory.newBasicCredentials(
getString(R.string.username),
getString(R.string.password)
)
);
mFilesAdapter = new FilesArrayAdapter(this, R.layout.file_in_list);
((ListView) findViewById(R.id.list_view)).setAdapter(mFilesAdapter);
// TODO move to background thread or task
AssetManager assets = getAssets();
try {
String sampleFileName = getString(R.string.sample_file_name);
File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path));
upFolder.mkdir();
File upFile = new File(upFolder, sampleFileName);
FileOutputStream fos = new FileOutputStream(upFile);
InputStream is = assets.open(sampleFileName);
int count;
byte[] buffer = new byte[1024];
while ((count = is.read(buffer, 0, buffer.length)) >= 0) {
fos.write(buffer, 0, count);
}
is.close();
fos.close();
} catch (IOException e) {
Toast.makeText(this, R.string.error_copying_sample_file, Toast.LENGTH_SHORT).show();
Timber.e(e, getString(R.string.error_copying_sample_file));
}
mFrame = findViewById(R.id.frame);
}
@Override
public void onDestroy() {
File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path));
File upFile = upFolder.listFiles()[0];
upFile.delete();
upFolder.delete();
super.onDestroy();
}
public void onClickHandler(View button) {
switch (button.getId()) {
case R.id.button_refresh:
startRefresh();
break;
case R.id.button_upload:
startUpload();
break;
case R.id.button_delete_remote:
startRemoteDeletion();
break;
case R.id.button_download:
startDownload();
break;
case R.id.button_delete_local:
startLocalDeletion();
break;
default:
Toast.makeText(this, R.string.youre_doing_it_wrong, Toast.LENGTH_SHORT).show();
}
}
private void startRefresh() {
ReadRemoteFolderOperation refreshOperation = new ReadRemoteFolderOperation(File.separator);
refreshOperation.execute(mClient, this, mHandler);
}
private void startUpload() {
File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path));
File fileToUpload = upFolder.listFiles()[0];
String remotePath = File.separator + fileToUpload.getName();
String mimeType = getString(R.string.sample_file_mimetype);
// Get the last modification date of the file from the file system
long timeStampLong = fileToUpload.lastModified() / 1000;
String timeStamp = Long.toString(timeStampLong);
UploadRemoteFileOperation uploadOperation = new UploadRemoteFileOperation(fileToUpload.getAbsolutePath(),
remotePath, mimeType, timeStamp);
uploadOperation.addDatatransferProgressListener(this);
uploadOperation.execute(mClient, this, mHandler);
}
private void startRemoteDeletion() {
File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path));
File fileToUpload = upFolder.listFiles()[0];
String remotePath = File.separator + fileToUpload.getName();
RemoveRemoteFileOperation removeOperation = new RemoveRemoteFileOperation(remotePath);
removeOperation.execute(mClient, this, mHandler);
}
private void startDownload() {
File downFolder = new File(getCacheDir(), getString(R.string.download_folder_path));
downFolder.mkdir();
File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path));
File fileToUpload = upFolder.listFiles()[0];
String remotePath = File.separator + fileToUpload.getName();
DownloadRemoteFileOperation downloadOperation = new DownloadRemoteFileOperation(remotePath,
downFolder.getAbsolutePath());
downloadOperation.addDatatransferProgressListener(this);
downloadOperation.execute(mClient, this, mHandler);
}
private void startLocalDeletion() {
File downFolder = new File(getCacheDir(), getString(R.string.download_folder_path));
File downloadedFile = downFolder.listFiles()[0];
if (!downloadedFile.delete() && downloadedFile.exists()) {
Toast.makeText(this, R.string.error_deleting_local_file, Toast.LENGTH_SHORT).show();
} else {
((TextView) findViewById(R.id.download_progress)).setText("0%");
findViewById(R.id.frame).setBackgroundDrawable(null);
}
}
@Override
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
if (!result.isSuccess()) {
Toast.makeText(this, R.string.todo_operation_finished_in_fail, Toast.LENGTH_SHORT).show();
Timber.e(result.getException(), result.getLogMessage());
} else if (operation instanceof ReadRemoteFolderOperation) {
onSuccessfulRefresh(result);
} else if (operation instanceof com.owncloud.android.lib.resources.files.UploadRemoteFileOperation) {
onSuccessfulUpload();
} else if (operation instanceof RemoveRemoteFileOperation) {
onSuccessfulRemoteDeletion();
} else if (operation instanceof DownloadRemoteFileOperation) {
onSuccessfulDownload();
} else {
Toast.makeText(this, R.string.todo_operation_finished_in_success, Toast.LENGTH_SHORT).show();
}
}
private void onSuccessfulRefresh(RemoteOperationResult result) {
mFilesAdapter.clear();
List<RemoteFile> files = new ArrayList<>();
for (RemoteFile remoteFile : (List<RemoteFile>) result.getData()) {
files.add(remoteFile);
}
for (RemoteFile file : files) {
mFilesAdapter.add(file);
}
mFilesAdapter.remove(mFilesAdapter.getItem(0));
mFilesAdapter.notifyDataSetChanged();
}
private void onSuccessfulUpload() {
startRefresh();
}
private void onSuccessfulRemoteDeletion() {
startRefresh();
TextView progressView = findViewById(R.id.upload_progress);
if (progressView != null) {
progressView.setText("0%");
}
}
private void onSuccessfulDownload() {
File downFolder = new File(getCacheDir(), getString(R.string.download_folder_path));
File downloadedFile = downFolder.listFiles()[0];
BitmapDrawable bDraw = new BitmapDrawable(getResources(), downloadedFile.getAbsolutePath());
mFrame.setBackgroundDrawable(bDraw);
}
@SuppressLint("SetTextI18n")
@Override
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
final long percentage = (totalToTransfer > 0 ? totalTransferredSoFar * 100 / totalToTransfer : 0);
final boolean upload = fileName.contains(getString(R.string.upload_folder_path));
Timber.d("progressRate %s", percentage);
mHandler.post(() -> {
TextView progressView;
if (upload) {
progressView = findViewById(R.id.upload_progress);
} else {
progressView = findViewById(R.id.download_progress);
}
if (progressView != null) {
progressView.setText(percentage + "%");
}
});
}
// user agent
@SuppressLint("StringFormatInvalid")
private String getUserAgent() {
String appString = getResources().getString(R.string.user_agent);
String packageName = getPackageName();
String version = "";
PackageInfo pInfo;
try {
pInfo = getPackageManager().getPackageInfo(packageName, 0);
if (pInfo != null) {
version = pInfo.versionName;
}
} catch (PackageManager.NameNotFoundException e) {
Timber.e(e);
}
// Mozilla/5.0 (Android) ownCloud-android/1.7.0
return String.format(appString, version);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2016 ownCloud GmbH.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>

View File

@ -1,122 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2016 ownCloud GmbH.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/button_refresh"
style="@style/ButtonStyle"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:onClick="onClickHandler"
android:text="@string/refresh"
/>
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/button_upload"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/button_refresh"
>
</ListView>
<Button
android:id="@+id/button_upload"
style="@style/ButtonStyle"
android:layout_above="@+id/frame"
android:layout_alignParentLeft="true"
android:onClick="onClickHandler"
android:text="@string/upload"
/>
<TextView
android:id="@+id/upload_progress"
style="@style/ProgressStyle"
android:layout_above="@id/frame"
android:layout_below="@id/list_view"
android:layout_toLeftOf="@+id/button_delete_remote"
android:layout_toRightOf="@id/button_upload"
android:gravity="center"
android:text="0%"
android:textSize="14sp"
/>
<Button
android:id="@id/button_delete_remote"
style="@style/ButtonStyle"
android:layout_above="@id/frame"
android:layout_alignParentRight="true"
android:onClick="onClickHandler"
android:text="@string/delete_remote_file"
/>
<FrameLayout
android:id="@id/frame"
android:layout_width="match_parent"
android:layout_height="@dimen/frame_height"
android:layout_above="@+id/button_download"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
>
</FrameLayout>
<Button
android:id="@id/button_download"
style="@style/ButtonStyle"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:onClick="onClickHandler"
android:text="@string/download"
/>
<TextView
android:id="@+id/download_progress"
style="@style/ProgressStyle"
android:layout_alignParentBottom="true"
android:layout_below="@id/frame"
android:layout_toLeftOf="@+id/button_delete_local"
android:layout_toRightOf="@id/button_download"
android:gravity="center"
android:text="0%"
android:textSize="14sp"
/>
<Button
android:id="@id/button_delete_local"
style="@style/ButtonStyle"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:onClick="onClickHandler"
android:text="@string/delete_local_file"
/>
</RelativeLayout>

View File

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2016 ownCloud GmbH.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="ButtonStyle" parent="@android:style/Widget.Holo.Button">
<item name="android:layout_width">120dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">12sp</item>
</style>
<style name="ProgressStyle" parent="@android:style/Widget.Holo.TextView">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">12sp</item>
</style>
</resources>

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2016 ownCloud GmbH.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<resources>
<dimen name="frame_height">120dp</dimen>
</resources>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2020 ownCloud GmbH.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<resources>
<string name="server_base_url"></string>
<string name="username"></string>
<string name="password"></string>
<string name="user_agent">Mozilla/5.0 (Android) ownCloud sample </string>
</resources>

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2016 ownCloud GmbH.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<resources>
<string name="app_name">ownCloud Sample Client</string>
<string name="refresh">Refresh</string>
<string name="upload">Upload</string>
<string name="delete_remote_file">Delete remote file</string>
<string name="download">Download</string>
<string name="delete_local_file">Delete local file</string>
<string name="youre_doing_it_wrong">You\'re doing it wrong</string>
<string name="todo_start_refresh">TODO: start refresh</string>
<string name="todo_start_upload">TODO: start upload</string>
<string name="todo_start_remote_deletion">TODO: start remote deletion</string>
<string name="todo_start_download">TODO: start download</string>
<string name="todo_start_local_deletion">TODO: start local deletion</string>
<string name="todo_operation_finished_in_success">TODO: operation finished in success</string>
<string name="todo_operation_finished_in_fail">TODO: operation finished in fail</string>
<string name="upload_folder_path">to_upload</string>
<string name="download_folder_path">downloaded</string>
<string name="error_copying_sample_file">Sample file could not be saved in temporal folder; upload will not work</string>
<string name="sample_file_name">oc_sample.png</string>
<string name="sample_file_mimetype">image/png</string>
<string name="error_deleting_local_file">Downloaded file could not be deleted</string>
</resources>

View File

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2016 ownCloud GmbH.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="ButtonStyle" parent="@android:style/Widget.Button">
<item name="android:layout_width">120dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">12sp</item>
</style>
<style name="ProgressStyle" parent="@android:style/Widget.TextView">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">12sp</item>
</style>
</resources>

View File

@ -1,2 +1 @@
include ':owncloudComLibrary' include ':owncloudComLibrary'
include ':sample_client'