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

Compare commits

...

79 Commits

Author SHA1 Message Date
Abel García de Prada
0ea053d612
Merge pull request #503 from owncloud/feature/open_in_web
[Feature] Open in web
2022-09-01 10:54:05 +02:00
Abel García de Prada
0be462c9aa Set private link capability to unknown type when it is not retrieved
The CapabilityBooleanType.fromBooleanValue set it to FALSE when the capability is not retrieved. We don't want this for this case. Probably neither for the rest, but changing that now would need deeper testing
2022-08-31 17:21:55 +02:00
Abel García de Prada
32ef5d2125 Add a new capability to allow/disallow private links 2022-08-31 17:21:55 +02:00
Abel García de Prada
6282fbd419 Add the open in web to the service facade 2022-08-31 17:21:55 +02:00
Abel García de Prada
6d235fe74a Add a new remote operation to retrieve the url to open a file with ocis provider 2022-08-31 17:21:55 +02:00
Abel García de Prada
9ab7c139e1 Retrieve the app providers from the capabilities 2022-08-31 17:21:55 +02:00
Abel García de Prada
24b850d20c
Merge pull request #496 from owncloud/propfind_with_shares
Retrieve share type directly within the propfind
2022-07-07 14:59:20 +02:00
Abel García de Prada
5be4eca0f7 Added a new property, so that, we can retrieve the shares within the propfind 2022-07-07 08:18:54 +02:00
Jesús Recio
2dc6f30a5e
Merge pull request #490 from owncloud/fix/eos
Fix unexpected end of stream when connecting with server
2022-05-31 11:48:55 +02:00
Abel García de Prada
f712a384cd Fix unexpected end of stream when connecting with server 2022-05-30 13:41:41 +02:00
Abel García de Prada
1f9bff1df0
Merge pull request #439 from owncloud/Modernize
Modernize
2022-05-30 09:36:35 +02:00
Hannes Achleitner
1412117dcd Android Arctic Fox 2022-05-30 09:12:42 +02:00
Hannes Achleitner
a054adc688 Java 8 is by default 2022-05-30 09:11:56 +02:00
Abel García de Prada
809e641b1a
Merge pull request #488 from owncloud/release/2.21-beta.1
[Release] 1.0.15-beta.1
2022-05-12 09:49:09 +02:00
Fernando Sanz
fd8caa42cb Updated versionCode and versionName 2022-05-11 09:53:47 +02:00
Abel García de Prada
45a9a190f8
Merge pull request #486 from owncloud/bump/kotlin_version
Bump kotlin version to 1.6.21
2022-05-09 14:43:16 +02:00
Abel García de Prada
2931a5494a Bump kotlin version to 1.6.21 2022-05-09 14:09:21 +02:00
Abel García de Prada
35fb64c1bd
Merge pull request #484 from owncloud/dependabot/gradle/org.jlleitschuh.gradle-ktlint-gradle-10.3.0
Bump ktlint-gradle from 10.2.1 to 10.3.0
2022-05-09 14:04:04 +02:00
dependabot[bot]
544fc6efd4
Bump ktlint-gradle from 10.2.1 to 10.3.0
Bumps ktlint-gradle from 10.2.1 to 10.3.0.

---
updated-dependencies:
- dependency-name: org.jlleitschuh.gradle:ktlint-gradle
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-04 07:13:39 +00:00
Abel García de Prada
16c31988e0
Merge pull request #485 from owncloud/dependabot/gradle/org.robolectric-robolectric-4.8.1
Bump robolectric from 4.8 to 4.8.1
2022-05-04 09:12:31 +02:00
dependabot[bot]
7fe41ce51c
Bump robolectric from 4.8 to 4.8.1
Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.8 to 4.8.1.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.8...robolectric-4.8.1)

---
updated-dependencies:
- dependency-name: org.robolectric:robolectric
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-04 02:08:05 +00:00
Abel García de Prada
aaafdb5ea2
Merge pull request #483 from owncloud/dependabot/gradle/org.robolectric-robolectric-4.8
Bump robolectric from 4.7.3 to 4.8
2022-04-29 08:28:09 +02:00
dependabot[bot]
6e503c2cf7
Bump robolectric from 4.7.3 to 4.8
Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.7.3 to 4.8.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.7.3...robolectric-4.8)

---
updated-dependencies:
- dependency-name: org.robolectric:robolectric
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-29 02:44:52 +00:00
Abel García de Prada
4de236b3c7
Merge pull request #364 from owncloud/fix/okhttp_singleton
remove okhttp singleton
2022-04-27 18:35:58 +02:00
Abel García de Prada
f6eb631fbf Reformat some code 2022-04-25 08:41:14 +02:00
Christian Schabesberger
be758eb5ea apply requested changes 2022-04-25 08:41:14 +02:00
Christian Schabesberger
d16ab2e643 remove sample client yet again 2022-04-25 08:41:14 +02:00
Christian Schabesberger
105830e4f0 apply requested changes 2022-04-25 08:41:14 +02:00
Christian Schabesberger
09375ce053 rebase okhttp_signleton fix on top of connection validator
fix token oauth token refresh error

pleasure the linter
2022-04-25 08:41:14 +02:00
Christian Schabesberger
fc4ae27bbe remove redundant intercept of httpclient with httpmethod 2022-04-25 08:41:14 +02:00
Schabi
344c1e1136 apply required fixes 2022-04-25 08:41:14 +02:00
Schabi
79e4287223 clean up http client 2022-04-25 08:41:14 +02:00
Christian Schabesberger
0313c1e103 get okhttp singleton removal changes to compile 2022-04-25 08:41:14 +02:00
Abel García de Prada
06e361afaa
Merge pull request #479 from owncloud/rename_check_script
rename verify_licnese_script back to check_script
2022-04-22 10:57:49 +02:00
Christian Schabesberger
217216e43f rename verify_licnese_script to check_code_script 2022-04-22 09:44:28 +02:00
Abel García de Prada
060a988978
Merge pull request #478 from owncloud/bump/gradle_kotlin
Bump gradle and kotlin to the latest versions
2022-04-11 09:29:57 +02:00
Abel García de Prada
e0b81bd11a Bump kotlin to 1.6.20 2022-04-08 10:53:23 +02:00
Abel García de Prada
97a613f168 Bump target sdk to api 31 2022-04-08 09:48:29 +02:00
Abel García de Prada
d8ac22d57e
Merge pull request #450 from owncloud/connection_validator
Connection validator
2022-03-31 14:55:31 +02:00
Christian Schabesberger
7c77c267f9 apply changes according to review 2022-03-23 13:27:19 +01:00
Christian Schabesberger
2a4195c966 fix lint and compile issues 2022-03-23 13:27:18 +01:00
Christian Schabesberger
d9dce81ce7 add GetBaseUrlRemoteOperation 2022-03-23 13:26:58 +01:00
Christian Schabesberger
4f3e167efa fix spelling mistakes 2022-03-23 13:26:58 +01:00
Christian Schabesberger
fc8440cc01 remove debug statements 2022-03-23 13:26:58 +01:00
Christian Schabesberger
5ca99a0e69 update base url in active client after 301 redirect
reduce validation retry count
2022-03-23 13:26:58 +01:00
Christian Schabesberger
ce761aaec2 remove redirect code from owncloudclient use okhttp instead 2022-03-23 13:26:58 +01:00
Christian Schabesberger
c59d1540c7 clean up credentials stuff from owncloudclient 2022-03-23 13:26:58 +01:00
Christian Schabesberger
5582097ca1 get access token to update through connection validator
update token
2022-03-23 13:26:58 +01:00
Christian Schabesberger
e27a968ddb add update authToken code to connectionValidator 2022-03-23 13:26:58 +01:00
Christian Schabesberger
cfd69987e9 update session from APM while running the app 2022-03-23 13:26:57 +01:00
Christian Schabesberger
b2f6d7f3b1 make initial check with apm work though connection validator 2022-03-23 13:24:27 +01:00
Christian Schabesberger
7ccb86c153 stop validation process if failing 2022-03-23 13:24:27 +01:00
Christian Schabesberger
e878ad3931 make oidc discovery work again 2022-03-23 13:24:27 +01:00
Schabi
8d09a5c242 make initial connection using connection validator work 2022-03-23 13:24:27 +01:00
Christian Schabesberger
ddb15a33f1 try shifting over to connection validator for updating credentials
intermediate commit
2022-03-23 13:24:27 +01:00
Christian Schabesberger
c2c351c912 set max redirect count for owncloud client to 5 2022-03-23 13:24:27 +01:00
Christian Schabesberger
ca206173df create logic scaffold for connection validator get status.php with it 2022-03-23 13:24:27 +01:00
Christian Schabesberger
7e4b43e7cb prepare code for the inclusion of connection validator 2022-03-23 13:24:27 +01:00
Christian Schabesberger
0e82f983b5 remove OwnCloudClient factory 2022-03-23 13:24:27 +01:00
Christian Schabesberger
0d94058db9 remove retrive cookies from middleware 2022-03-23 13:24:27 +01:00
Christian Schabesberger
2f952a3a09 debug and add mutex for halding requiests 2022-03-23 13:24:27 +01:00
Christian Schabesberger
39b1fce7f6 init connectionvalidator 2022-03-23 13:24:27 +01:00
Abel García de Prada
351efb7bf2
Merge pull request #474 from owncloud/dependabot/gradle/com.facebook.stetho-stetho-okhttp3-1.6.0
Bump stetho-okhttp3 from 1.5.1 to 1.6.0
2022-03-10 11:30:54 +01:00
dependabot[bot]
2edfc17653
Bump stetho-okhttp3 from 1.5.1 to 1.6.0
Bumps [stetho-okhttp3](https://github.com/facebook/stetho) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/facebook/stetho/releases)
- [Changelog](https://github.com/facebook/stetho/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/stetho/compare/v1.5.1...v1.6.0)

---
updated-dependencies:
- dependency-name: com.facebook.stetho:stetho-okhttp3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-10 10:15:54 +00:00
Abel García de Prada
e0efa7b3ab
Merge pull request #472 from owncloud/feature/check_script
Feature/check script
2022-03-10 10:21:07 +01:00
Abel García de Prada
5ab6514164 Rename the script and function to clarify their purpose 2022-03-10 09:45:34 +01:00
Christian Schabesberger
194894637e fix wrongly licensed source files 2022-03-10 09:45:34 +01:00
Christian Schabesberger
c662383182 add mit license header to where it was missing 2022-03-10 09:45:34 +01:00
Christian Schabesberger
f8c7c12f5b add check_script 2022-03-10 09:45:34 +01:00
Abel García de Prada
61508c85d1
Merge pull request #469 from owncloud/feature/add_stetho
Feature/add stetho
2022-03-08 09:27:34 +01:00
Christian Schabesberger
5619c1daf8 apply code cleanup according to review 2022-03-08 08:59:10 +01:00
Christian Schabesberger
85a3918ff1 fix usage of debug interceptor 2022-03-08 08:59:10 +01:00
Christian Schabesberger
7e507abf32 add debug interceptor 2022-03-08 08:59:10 +01:00
Abel García de Prada
a7e9138593
Merge pull request #471 from owncloud/fix/remove_sample_client
remove sample client
2022-03-08 08:58:00 +01:00
Christian Schabesberger
0ae5cf5ca2 remove sample client 2022-03-08 08:49:26 +01:00
Abel García de Prada
063e7366e5
Merge pull request #473 from owncloud/dependabot/github_actions/actions/checkout-3
Bump actions/checkout from 2 to 3
2022-03-07 07:52:38 +01:00
dependabot[bot]
5cec7b43e2
Bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-07 02:03:39 +00:00
Abel García de Prada
ff0cde35fe
Merge pull request #466 from owncloud/release/1.0.14
[Release] 1.0.14
2022-02-16 10:45:45 +01:00
Fernando Sanz
a9a6ca6d4e Upgraded versionName and versionNumber 2022-02-09 14:32:09 +01:00
77 changed files with 1181 additions and 1394 deletions

View File

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

View File

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

15
check_code_script.sh Executable file
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") {
exclude module: "kotlin-reflect"
}
implementation 'org.apache.commons:commons-lang3:3.12.0'
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.7.3'
testImplementation 'org.robolectric:robolectric:4.8.1'
debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.6.0'
}
android {
compileSdkVersion 30
compileSdkVersion 31
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode = 10001300
versionName = "1.0.13"
targetSdkVersion 31
}
lintOptions {
lint {
abortOnError false
ignoreWarnings true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
testOptions {
unitTests {
includeAndroidResources = true

View File

@ -19,40 +19,12 @@
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common;
package com.owncloud.android.lib.common.http
import android.content.Context;
import android.net.Uri;
import com.facebook.stetho.okhttp3.StethoInterceptor
import com.owncloud.android.lib.common.http.HttpClient;
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation;
public class OwnCloudClientFactory {
/**
* Creates a OwnCloudClient to access a URL and sets the desired parameters for ownCloud
* client connections.
*
* @param uri URL to the ownCloud server; BASE ENTRY POINT, not WebDavPATH
* @param context Android context where the OwnCloudClient is being created.
* @return A OwnCloudClient object ready to be used
*/
public static OwnCloudClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) {
OwnCloudClient client = new OwnCloudClient(uri);
client.setFollowRedirects(followRedirects);
HttpClient.setContext(context);
retrieveCookiesFromMiddleware(client);
return client;
}
private static void retrieveCookiesFromMiddleware(OwnCloudClient client) {
final GetRemoteStatusOperation statusOperation = new GetRemoteStatusOperation();
statusOperation.run(client);
}
object DebugInterceptorFactory {
fun getInterceptor() = StethoInterceptor()
}

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;
import android.accounts.AccountManager;
import android.accounts.AccountsException;
import android.content.Context;
import android.net.Uri;
import at.bitfire.dav4jvm.exception.HttpException;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
@ -37,7 +35,6 @@ import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory
import com.owncloud.android.lib.common.http.HttpClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import com.owncloud.android.lib.common.network.RedirectionPath;
import com.owncloud.android.lib.common.utils.RandomUtils;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import okhttp3.Cookie;
@ -49,40 +46,54 @@ import java.io.InputStream;
import java.util.List;
import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER;
import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID;
import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY;
public class OwnCloudClient extends HttpClient {
public static final String WEBDAV_FILES_PATH_4_0 = "/remote.php/dav/files/";
public static final String WEBDAV_PATH_4_0_AND_LATER = "/remote.php/dav";
public static final String STATUS_PATH = "/status.php";
private static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/";
private static final int MAX_REDIRECTIONS_COUNT = 3;
private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1;
private static final int MAX_RETRY_COUNT = 2;
private static byte[] sExhaustBuffer = new byte[1024];
private static int sIntanceCounter = 0;
private OwnCloudCredentials mCredentials = null;
private int mInstanceNumber;
private Uri mBaseUri;
private OwnCloudVersion mVersion = null;
private OwnCloudAccount mAccount;
private final ConnectionValidator mConnectionValidator;
private Object mRequestMutex = new Object();
// If set to true a mutex will be used to prevent parallel execution of the execute() method
// if false the execute() method can be called even though the mutex is already aquired.
// This is used for the ConnectionValidator, which has to be able to execute OperationsWhile all "normal" operations net
// to be set on hold.
private final Boolean mSynchronizeRequests;
private SingleSessionManager mSingleSessionManager = null;
private boolean mFollowRedirects;
private boolean mFollowRedirects = false;
public OwnCloudClient(Uri baseUri,
ConnectionValidator connectionValidator,
boolean synchronizeRequests,
SingleSessionManager singleSessionManager,
Context context) {
super(context);
public OwnCloudClient(Uri baseUri) {
if (baseUri == null) {
throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL");
}
mBaseUri = baseUri;
mSynchronizeRequests = synchronizeRequests;
mSingleSessionManager = singleSessionManager;
mInstanceNumber = sIntanceCounter++;
Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient");
clearCredentials();
clearCookies();
mConnectionValidator = connectionValidator;
}
public void clearCredentials() {
@ -92,11 +103,27 @@ public class OwnCloudClient extends HttpClient {
}
public int executeHttpMethod(HttpBaseMethod method) throws Exception {
boolean repeatWithFreshCredentials;
if (mSynchronizeRequests) {
synchronized (mRequestMutex) {
return saveExecuteHttpMethod(method);
}
} else {
return saveExecuteHttpMethod(method);
}
}
private int saveExecuteHttpMethod(HttpBaseMethod method) throws Exception {
int repeatCounter = 0;
int status;
if (mFollowRedirects) {
method.setFollowRedirects(true);
}
boolean retry;
do {
repeatCounter++;
retry = false;
String requestId = RandomUtils.generateRandomUUID();
// Header to allow tracing requests in apache and ownCloud logs
@ -104,105 +131,34 @@ public class OwnCloudClient extends HttpClient {
method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId);
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
if (mCredentials.getHeaderAuth() != null && method.getRequestHeader(AUTHORIZATION_HEADER) == null) {
if (mCredentials.getHeaderAuth() != null && !mCredentials.getHeaderAuth().isEmpty()) {
method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
}
status = method.execute();
if (mFollowRedirects) {
status = followRedirection(method).getLastStatus();
status = method.execute(this);
if (shouldConnectionValidatorBeCalled(method, status)) {
retry = mConnectionValidator.validate(this, mSingleSessionManager, getContext()); // retry on success fail on no success
} else if (method.getFollowPermanentRedirects() && status == HTTP_MOVED_PERMANENTLY) {
retry = true;
method.setFollowRedirects(true);
}
repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter);
if (repeatWithFreshCredentials) {
repeatCounter++;
}
} while (repeatWithFreshCredentials);
} while (retry && repeatCounter < MAX_RETRY_COUNT);
return status;
}
private int executeRedirectedHttpMethod(HttpBaseMethod method) throws Exception {
boolean repeatWithFreshCredentials;
int repeatCounter = 0;
int status;
private boolean shouldConnectionValidatorBeCalled(HttpBaseMethod method, int status) {
do {
String requestId = RandomUtils.generateRandomUUID();
// Header to allow tracing requests in apache and ownCloud logs
Timber.d("Executing in request with id %s", requestId);
method.setRequestHeader(OC_X_REQUEST_ID, requestId);
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
if (mCredentials.getHeaderAuth() != null) {
method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
}
status = method.execute();
repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter);
if (repeatWithFreshCredentials) {
repeatCounter++;
}
} while (repeatWithFreshCredentials);
return status;
}
public RedirectionPath followRedirection(HttpBaseMethod method) throws Exception {
int redirectionsCount = 0;
int status = method.getStatusCode();
RedirectionPath redirectionPath = new RedirectionPath(status, MAX_REDIRECTIONS_COUNT);
while (redirectionsCount < MAX_REDIRECTIONS_COUNT &&
(status == HttpConstants.HTTP_MOVED_PERMANENTLY ||
status == HttpConstants.HTTP_MOVED_TEMPORARILY ||
status == HttpConstants.HTTP_TEMPORARY_REDIRECT)
) {
final String location = method.getResponseHeader(HttpConstants.LOCATION_HEADER) != null
? method.getResponseHeader(HttpConstants.LOCATION_HEADER)
: method.getResponseHeader(HttpConstants.LOCATION_HEADER_LOWER);
if (location != null) {
Timber.d("#" + mInstanceNumber + "Location to redirect: " + location);
redirectionPath.addLocation(location);
// Release the connection to avoid reach the max number of connections per host
// due to it will be set a different url
exhaustResponse(method.getResponseBodyAsStream());
method.setUrl(HttpUrl.parse(location));
final String destination = method.getRequestHeader("Destination") != null
? method.getRequestHeader("Destination")
: method.getRequestHeader("destination");
if (destination != null) {
final int suffixIndex = location.lastIndexOf(getUserFilesWebDavUri().toString());
final String redirectionBase = location.substring(0, suffixIndex);
final String destinationPath = destination.substring(mBaseUri.toString().length());
method.setRequestHeader("destination", redirectionBase + destinationPath);
}
try {
status = executeRedirectedHttpMethod(method);
} catch (HttpException e) {
if (e.getMessage().contains(Integer.toString(HttpConstants.HTTP_MOVED_TEMPORARILY))) {
status = HttpConstants.HTTP_MOVED_TEMPORARILY;
} else {
throw e;
}
}
redirectionPath.addStatus(status);
redirectionsCount++;
} else {
Timber.d(" #" + mInstanceNumber + "No location to redirect!");
status = HttpConstants.HTTP_NOT_FOUND;
}
}
return redirectionPath;
return mConnectionValidator != null && (
(!(mCredentials instanceof OwnCloudAnonymousCredentials) &&
status == HttpConstants.HTTP_UNAUTHORIZED
) || (!mFollowRedirects &&
!method.getFollowRedirects() &&
status == HttpConstants.HTTP_MOVED_TEMPORARILY
)
);
}
/**
@ -273,26 +229,18 @@ public class OwnCloudClient extends HttpClient {
}
}
public String getCookiesString() {
StringBuilder cookiesString = new StringBuilder();
List<Cookie> cookieList = getCookiesFromUrl(HttpUrl.parse(mBaseUri.toString()));
if (cookieList != null) {
for (Cookie cookie : cookieList) {
cookiesString.append(cookie.toString()).append(";");
}
}
return cookiesString.toString();
}
public void setCookiesForCurrentAccount(List<Cookie> cookies) {
public void setCookiesForBaseUri(List<Cookie> cookies) {
getOkHttpClient().cookieJar().saveFromResponse(
HttpUrl.parse(getAccount().getBaseUri().toString()),
HttpUrl.parse(mBaseUri.toString()),
cookies
);
}
public List<Cookie> getCookiesForBaseUri() {
return getOkHttpClient().cookieJar().loadForRequest(
HttpUrl.parse(mBaseUri.toString()));
}
public OwnCloudVersion getOwnCloudVersion() {
return mVersion;
}
@ -309,95 +257,7 @@ public class OwnCloudClient extends HttpClient {
this.mAccount = account;
}
/**
* Checks the status code of an execution and decides if should be repeated with fresh credentials.
* <p>
* Invalidates current credentials if the request failed as anauthorized.
* <p>
* Refresh current credentials if possible, and marks a retry.
*
* @param status
* @param repeatCounter
* @return
*/
private boolean checkUnauthorizedAccess(int status, int repeatCounter) {
boolean credentialsWereRefreshed = false;
if (shouldInvalidateAccountCredentials(status)) {
boolean invalidated = invalidateAccountCredentials();
if (invalidated) {
if (getCredentials().authTokenCanBeRefreshed() &&
repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) {
try {
mAccount.loadCredentials(getContext());
// if mAccount.getCredentials().length() == 0 --> refresh failed
setCredentials(mAccount.getCredentials());
credentialsWereRefreshed = true;
} catch (AccountsException | IOException e) {
Timber.e(e, "Error while trying to refresh auth token for %s",
mAccount.getSavedAccount().name
);
}
}
if (!credentialsWereRefreshed && mSingleSessionManager != null) {
// if credentials are not refreshed, client must be removed
// from the OwnCloudClientManager to prevent it is reused once and again
mSingleSessionManager.removeClientFor(mAccount);
}
}
// else: onExecute will finish with status 401
}
return credentialsWereRefreshed;
}
/**
* Determines if credentials should be invalidated according the to the HTTPS status
* of a network request just performed.
*
* @param httpStatusCode Result of the last request ran with the 'credentials' belows.
* @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or
* cannot be invalidated with the given arguments.
*/
private boolean shouldInvalidateAccountCredentials(int httpStatusCode) {
boolean shouldInvalidateAccountCredentials =
(httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED);
shouldInvalidateAccountCredentials &= (mCredentials != null && // real credentials
!(mCredentials instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials));
// test if have all the needed to effectively invalidate ...
shouldInvalidateAccountCredentials &= (mAccount != null && mAccount.getSavedAccount() != null && getContext() != null);
return shouldInvalidateAccountCredentials;
}
/**
* Invalidates credentials stored for the given account in the system {@link AccountManager} and in
* current {@link SingleSessionManager#getDefaultSingleton()} instance.
* <p>
* {@link #shouldInvalidateAccountCredentials(int)} should be called first.
*
* @return 'True' if invalidation was successful, 'false' otherwise.
*/
private boolean invalidateAccountCredentials() {
AccountManager am = AccountManager.get(getContext());
am.invalidateAuthToken(
mAccount.getSavedAccount().type,
mCredentials.getAuthToken()
);
am.clearPassword(mAccount.getSavedAccount()); // being strict, only needed for Basic Auth credentials
return true;
}
public boolean followRedirects() {
return mFollowRedirects;
}
public void setFollowRedirects(boolean followRedirects) {
this.mFollowRedirects = followRedirects;
}
}
}

View File

@ -49,6 +49,7 @@ public class SingleSessionManager {
private static SingleSessionManager sDefaultSingleton;
private static String sUserAgent;
private static ConnectionValidator sConnectionValidator;
private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername = new ConcurrentHashMap<>();
private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername = new ConcurrentHashMap<>();
@ -60,6 +61,14 @@ public class SingleSessionManager {
return sDefaultSingleton;
}
public static void setConnectionValidator(ConnectionValidator connectionValidator) {
sConnectionValidator = connectionValidator;
}
public static ConnectionValidator getConnectionValidator() {
return sConnectionValidator;
}
public static String getUserAgent() {
return sUserAgent;
}
@ -68,7 +77,23 @@ public class SingleSessionManager {
sUserAgent = userAgent;
}
public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws OperationCanceledException,
private static OwnCloudClient createOwnCloudClient(Uri uri,
Context context,
ConnectionValidator connectionValidator,
SingleSessionManager singleSessionManager) {
OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true, singleSessionManager, context);
return client;
}
public OwnCloudClient getClientFor(OwnCloudAccount account,
Context context) throws OperationCanceledException,
AuthenticatorException, IOException {
return getClientFor(account, context, getConnectionValidator());
}
public OwnCloudClient getClientFor(OwnCloudAccount account,
Context context,
ConnectionValidator connectionValidator) throws OperationCanceledException,
AuthenticatorException, IOException {
Timber.d("getClientFor starting ");
@ -104,10 +129,11 @@ public class SingleSessionManager {
if (client == null) {
// no client to reuse - create a new one
client = OwnCloudClientFactory.createOwnCloudClient(
client = createOwnCloudClient(
account.getBaseUri(),
context.getApplicationContext(),
true); // TODO remove dependency on OwnCloudClientFactory
context,
connectionValidator,
this); // TODO remove dependency on OwnCloudClientFactory
//the next two lines are a hack because okHttpclient is used as a singleton instead of being an
//injected instance that can be deleted when required
@ -115,7 +141,6 @@ public class SingleSessionManager {
client.clearCredentials();
client.setAccount(account);
HttpClient.setContext(context);
account.loadCredentials(context);
client.setCredentials(account.getCredentials());

View File

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

View File

@ -21,26 +21,11 @@
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.sampleclient;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
package com.owncloud.android.lib.common.http
import com.owncloud.android.lib.resources.files.RemoteFile;
public class FilesArrayAdapter extends ArrayAdapter<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;
}
}
import okhttp3.Interceptor
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.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
@ -54,32 +53,46 @@ import java.util.concurrent.TimeUnit;
*/
public class HttpClient {
private static OkHttpClient sOkHttpClient;
private static Context sContext;
private static HashMap<String, List<Cookie>> sCookieStore = new HashMap<>();
private static LogInterceptor sLogInterceptor;
private Context mContext;
private HashMap<String, List<Cookie>> mCookieStore = new HashMap<>();
private LogInterceptor mLogInterceptor = new LogInterceptor();
public static OkHttpClient getOkHttpClient() {
if (sOkHttpClient == null) {
try {
final X509TrustManager trustManager = new AdvancedX509TrustManager(
NetworkUtils.getKnownServersStore(sContext));
final SSLSocketFactory sslSocketFactory = getNewSslSocketFactory(trustManager);
// Automatic cookie handling, NOT PERSISTENT
final CookieJar cookieJar = new CookieJarImpl(sCookieStore);
private OkHttpClient mOkHttpClient = null;
// TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok.
//.hostnameVerifier(new BrowserCompatHostnameVerifier());
sOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar);
} catch (Exception e) {
Timber.e(e, "Could not setup SSL system.");
}
protected HttpClient(Context context) {
if (context == null) {
Timber.e("Context may not be NULL!");
throw new NullPointerException("Context may not be NULL!");
}
return sOkHttpClient;
mContext = context;
}
private static SSLContext getSslContext() throws NoSuchAlgorithmException {
public OkHttpClient getOkHttpClient() {
if (mOkHttpClient == null) {
try {
final X509TrustManager trustManager = new AdvancedX509TrustManager(
NetworkUtils.getKnownServersStore(mContext));
final SSLContext sslContext = buildSSLContext();
sslContext.init(null, new TrustManager[]{trustManager}, null);
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
// Automatic cookie handling, NOT PERSISTENT
final CookieJar cookieJar = new CookieJarImpl(mCookieStore);
mOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar);
} catch (NoSuchAlgorithmException nsae) {
Timber.e(nsae, "Could not setup SSL system.");
throw new RuntimeException("Could not setup okHttp client.", nsae);
} catch (Exception e) {
Timber.e(e, "Could not setup okHttp client.");
throw new RuntimeException("Could not setup okHttp client.", e);
}
}
return mOkHttpClient;
}
private SSLContext buildSSLContext() throws NoSuchAlgorithmException {
try {
return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName());
} catch (NoSuchAlgorithmException tlsv13Exception) {
@ -100,17 +113,11 @@ public class HttpClient {
}
}
private static SSLSocketFactory getNewSslSocketFactory(X509TrustManager trustManager)
throws NoSuchAlgorithmException, KeyManagementException {
final SSLContext sslContext = getSslContext();
sslContext.init(null, new TrustManager[]{trustManager}, null);
return sslContext.getSocketFactory();
}
private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager,
CookieJar cookieJar) {
private OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager,
CookieJar cookieJar) {
return new OkHttpClient.Builder()
.addNetworkInterceptor(getLogInterceptor())
.addNetworkInterceptor(DebugInterceptorFactory.INSTANCE.getInterceptor())
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
@ -122,26 +129,19 @@ public class HttpClient {
.build();
}
public static LogInterceptor getLogInterceptor() {
if (sLogInterceptor == null) {
sLogInterceptor = new LogInterceptor();
}
return sLogInterceptor;
}
public static List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
return sCookieStore.get(httpUrl.host());
}
public Context getContext() {
return sContext;
return mContext;
}
public static void setContext(Context context) {
sContext = context;
public LogInterceptor getLogInterceptor() {
return mLogInterceptor;
}
public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
return mCookieStore.get(httpUrl.host());
}
public void clearCookies() {
sCookieStore.clear();
mCookieStore.clear();
}
}

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,10 +26,11 @@ package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.PropertyUtils.getAllPropSet
import at.bitfire.dav4jvm.PropertyUtils.getQuotaPropset
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
object DavUtils {
@JvmStatic val allPropset: Array<Property.Name>
get() = getAllPropSet()
get() = getAllPropSet().plus(OCShareTypes.NAME)
val quotaPropSet: Array<Property.Name>
get() = getQuotaPropset()

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,44 @@
/* 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.webdav.properties
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.XmlUtils
import org.xmlpull.v1.XmlPullParser
class OCShareTypes : ShareTypeListProperty() {
class Factory : ShareTypeListProperty.Factory() {
override fun create(parser: XmlPullParser) =
create(parser, OCShareTypes())
override fun getName(): Property.Name = NAME
}
companion object {
@JvmField
val NAME = Property.Name(XmlUtils.NS_OWNCLOUD, "share-types")
}
}

View File

@ -0,0 +1,46 @@
/* 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.webdav.properties
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.PropertyFactory
import at.bitfire.dav4jvm.XmlUtils
import org.xmlpull.v1.XmlPullParser
import java.util.LinkedList
abstract class ShareTypeListProperty : Property {
val shareTypes = LinkedList<String>()
override fun toString() = "share types =[" + shareTypes.joinToString(", ") + "]"
abstract class Factory : PropertyFactory {
fun create(parser: XmlPullParser, list: ShareTypeListProperty): ShareTypeListProperty {
XmlUtils.readTextPropertyList(parser, Property.Name(XmlUtils.NS_OWNCLOUD, "share-type"), list.shareTypes)
return list
}
}
}

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

View File

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

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
import info.hannes.timber.FileLoggingTree

View File

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

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

View File

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

View File

@ -87,7 +87,8 @@ public class CreateRemoteFolderOperation extends RemoteOperation {
RemoteOperationResult result;
try {
Uri webDavUri = createChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri();
final MkColMethod mkcol = new MkColMethod(new URL(webDavUri + WebdavUtils.encodePath(mRemotePath)));
final MkColMethod mkcol = new MkColMethod(
new URL(webDavUri + WebdavUtils.encodePath(mRemotePath)));
mkcol.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
mkcol.setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
final int status = client.executeHttpMethod(mkcol);

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

@ -0,0 +1,98 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.nonwebdav.PostMethod
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation.OpenInWebParams.Companion.PARAM_FILE_ID
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import okhttp3.FormBody
import okhttp3.RequestBody
import timber.log.Timber
import java.net.URL
import java.util.concurrent.TimeUnit
class GetUrlToOpenInWebRemoteOperation(
val openWithWebEndpoint: String,
val fileId: String,
) : RemoteOperation<String>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
return try {
val openInWebRequestBody = OpenInWebParams(fileId).toRequestBody()
val stringUrl = client.baseUri.toString() + WebdavUtils.encodePath(openWithWebEndpoint) + "?$PARAM_FILE_ID=$fileId"
val postMethod = PostMethod(URL(stringUrl), openInWebRequestBody).apply {
setReadTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
setConnectionTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
}
val status = client.executeHttpMethod(postMethod)
Timber.d("Open in web for file: $fileId - $status${if (!isSuccess(status)) "(FAIL)" else ""}")
if (isSuccess(status)) RemoteOperationResult<String>(ResultCode.OK).apply {
val moshi = Moshi.Builder().build()
val adapter: JsonAdapter<OpenInWebResponse> = moshi.adapter(OpenInWebResponse::class.java)
data = postMethod.getResponseBodyAsString()?.let { adapter.fromJson(it)!!.uri }
}
else RemoteOperationResult<String>(postMethod).apply { data = "" }
} catch (e: Exception) {
val result = RemoteOperationResult<String>(e)
Timber.e(e, "Open in web for file: $fileId failed")
result
}
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
data class OpenInWebParams(val fileId: String) {
fun toRequestBody(): RequestBody =
FormBody.Builder().build()
companion object {
const val PARAM_FILE_ID = "file_id"
}
}
@JsonClass(generateAdapter = true)
data class OpenInWebResponse(val uri: String)
companion object {
/**
* Maximum time to wait for a response from the server in milliseconds.
*/
private const val TIMEOUT = 5_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
try {
// remote request
propfind = new PropfindMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
propfind = new PropfindMethod(
new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
DEPTH_0,
DavUtils.getAllPropset());

View File

@ -24,6 +24,7 @@
package com.owncloud.android.lib.resources.files;
import at.bitfire.dav4jvm.PropertyRegistry;
import at.bitfire.dav4jvm.Response;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.accounts.AccountUtils;
@ -31,6 +32,7 @@ import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.webdav.DavConstants;
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.http.methods.webdav.properties.OCShareTypes;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
@ -72,13 +74,12 @@ public class ReadRemoteFolderOperation extends RemoteOperation<ArrayList<RemoteF
RemoteOperationResult<ArrayList<RemoteFile>> result = null;
try {
PropertyRegistry.INSTANCE.register(OCShareTypes.Factory.class.newInstance());
PropfindMethod propfindMethod = new PropfindMethod(
new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
DavConstants.DEPTH_1,
DavUtils.getAllPropset());
client.setFollowRedirects(true);
int status = client.executeHttpMethod(propfindMethod);
if (isSuccess(status)) {

View File

@ -40,10 +40,14 @@ import at.bitfire.dav4jvm.property.OCPrivatelink;
import at.bitfire.dav4jvm.property.OCSize;
import at.bitfire.dav4jvm.property.QuotaAvailableBytes;
import at.bitfire.dav4jvm.property.QuotaUsedBytes;
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes;
import com.owncloud.android.lib.resources.shares.ShareType;
import timber.log.Timber;
import java.io.File;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.LinkedList;
import java.util.List;
/**
@ -85,6 +89,8 @@ public class RemoteFile implements Parcelable, Serializable {
private BigDecimal mQuotaUsedBytes;
private BigDecimal mQuotaAvailableBytes;
private String mPrivateLink;
private boolean mSharedByLink;
private boolean mSharedWithSharee;
public RemoteFile() {
resetData();
@ -153,6 +159,23 @@ public class RemoteFile implements Parcelable, Serializable {
if (property instanceof OCPrivatelink) {
this.setPrivateLink(((OCPrivatelink) property).getLink());
}
if (property instanceof OCShareTypes) {
LinkedList<String> list = ((OCShareTypes) property).getShareTypes();
for (int i = 0; i < list.size(); i++) {
ShareType shareType = ShareType.Companion.fromValue(Integer.parseInt(list.get(i)));
if (shareType == null) {
Timber.d("Illegal share type value: " + list.get(i));
continue;
}
if (shareType.equals(ShareType.PUBLIC_LINK)) {
this.setSharedViaLink(true);
} else if (shareType.equals(ShareType.USER) ||
shareType.equals(ShareType.FEDERATED) ||
shareType.equals(ShareType.GROUP)) {
this.setSharedWithSharee(true);
}
}
}
}
}
@ -266,6 +289,22 @@ public class RemoteFile implements Parcelable, Serializable {
mPrivateLink = privateLink;
}
public void setSharedWithSharee(boolean shareWithSharee) {
mSharedWithSharee = shareWithSharee;
}
public boolean isSharedWithSharee() {
return mSharedWithSharee;
}
public void setSharedViaLink(boolean sharedViaLink) {
mSharedByLink = sharedViaLink;
}
public boolean isSharedByLink() {
return mSharedByLink;
}
/**
* Used internally. Reset all file properties
*/
@ -282,6 +321,8 @@ public class RemoteFile implements Parcelable, Serializable {
mQuotaUsedBytes = null;
mQuotaAvailableBytes = null;
mPrivateLink = null;
mSharedWithSharee = false;
mSharedByLink = false;
}
public void readFromParcel(Parcel source) {
@ -319,4 +360,4 @@ public class RemoteFile implements Parcelable, Serializable {
dest.writeSerializable(mQuotaAvailableBytes);
dest.writeString(mPrivateLink);
}
}
}

View File

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

View File

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

View File

@ -92,7 +92,8 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation
result = new RemoteOperationResult<>(new OperationCancelledException());
break;
} else {
mPutMethod = new PutMethod(new URL(uriPrefix + File.separator + chunkIndex), mFileRequestBody);
mPutMethod = new PutMethod(
new URL(uriPrefix + File.separator + chunkIndex), mFileRequestBody);
if (chunkIndex == chunkCount - 1) {
// Added a high timeout to the last chunk due to when the last chunk

View File

@ -28,4 +28,5 @@ import com.owncloud.android.lib.resources.Service
interface FileService : Service {
fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean>
fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult<String>
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@ -66,7 +66,9 @@ data class RemoteCapability(
// Files
var filesBigFileChunking: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesUndelete: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesVersioning: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN
var filesVersioning: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
val filesPrivateLinks: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
val filesAppProviders: List<RemoteOCISProvider>?,
) {
/**
* Enum for Boolean Type in capabilities, with values:
@ -98,4 +100,13 @@ data class RemoteCapability(
}
}
}
data class RemoteOCISProvider(
val enabled: Boolean,
val version: String,
val appsUrl: String?,
val openUrl: String?,
val openWebUrl: String?,
val newUrl: String?,
)
}

View File

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

View File

@ -26,6 +26,7 @@ package com.owncloud.android.lib.resources.status.responses
import com.owncloud.android.lib.resources.status.RemoteCapability
import com.owncloud.android.lib.resources.status.RemoteCapability.CapabilityBooleanType
import com.owncloud.android.lib.resources.status.RemoteCapability.RemoteOCISProvider
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@ -68,6 +69,8 @@ data class CapabilityResponse(
filesBigFileChunking = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.bigfilechunking),
filesUndelete = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.undelete),
filesVersioning = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.versioning),
filesPrivateLinks = capabilities?.fileCapabilities?.privateLinks?.let { CapabilityBooleanType.fromBooleanValue(it) } ?: CapabilityBooleanType.UNKNOWN,
filesAppProviders = capabilities?.fileCapabilities?.appProviders?.map { it.toOCISProvider() },
filesSharingFederationIncoming = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.incoming),
filesSharingFederationOutgoing = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.outgoing),
filesSharingUserProfilePicture = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingUser?.profilePicture),
@ -160,9 +163,28 @@ data class FileSharingUser(
data class FileCapabilities(
val bigfilechunking: Boolean?,
val undelete: Boolean?,
val versioning: Boolean?
val versioning: Boolean?,
val privateLinks: Boolean?,
@Json(name = "app_providers")
val appProviders: List<AppProvider>?
)
@JsonClass(generateAdapter = true)
data class AppProvider(
val enabled: Boolean,
val version: String,
@Json(name = "apps_url")
val appsUrl: String?,
@Json(name = "open_url")
val openUrl: String?,
@Json(name = "open_web_url")
val openWebUrl: String?,
@Json(name = "new_url")
val newUrl: String?,
) {
fun toOCISProvider() = RemoteOCISProvider(enabled, version, appsUrl, openUrl, openWebUrl, newUrl)
}
@JsonClass(generateAdapter = true)
data class DavCapabilities(
val chunking: String?

View File

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

View File

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

View File

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

View File

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

View File

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

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 ':sample_client'