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

Compare commits

..

88 Commits
2.0 ... master

Author SHA1 Message Date
Aitorbp
71224c30d1
Merge pull request #581 from owncloud/release/4.1
[RELEASE] 4.1 library to master
2023-09-05 09:49:55 +01:00
Aitorbp
c82d75b1d8 New fix for snackbar when copy without server on 2023-08-30 10:16:58 +01:00
Manuel Plazas Palacio
530999848f Solving bugs when getting file existence in OC10 2023-08-24 14:12:30 +02:00
Manuel Plazas Palacio
b4625d017c Solving bugs when getting file existence. 2023-08-24 12:28:23 +02:00
Manuel Plazas Palacio
2c698cc2a4
Merge pull request #578 from owncloud/feature/unneccesary_call
[BUG] unneccessary or wrong call ....
2023-08-23 10:56:22 +02:00
Manuel Plazas Palacio
bf183fe04d Solving bug when copyin or moving 2023-08-22 15:24:59 +02:00
Manuel Plazas Palacio
1f8de383b6 Removing extra path on check path existence. 2023-08-18 11:14:11 +02:00
Aitorbp
8eb435a1c2
Merge pull request #577 from owncloud/feature/respect_app_provider_values_from_capabilities
[FEATURE REQUEST] Respect app_provider values from capabilities
2023-08-03 08:26:26 +01:00
Aitorbp
052566d205 Added variable from local data source 2023-07-31 12:43:16 +01:00
Manuel Plazas Palacio
063c3fa9e9
Merge pull request #571 from owncloud/feature/copy_move_conflic_solved_users
[FEATURE REQUEST] Copy/move conflict solved by users
2023-06-21 08:33:47 +02:00
Manuel Plazas Palacio
fa143804d2 Solving CR changes. 2023-06-08 13:31:09 +02:00
Manuel Plazas Palacio
5b64876e2c Showing decision dialog when moving file conflict.
Solving errors when copying, replace and keep both.
2023-06-07 12:15:14 +02:00
Manuel Plazas Palacio
d81ca97f14 Showing decision dialog when copying file conflict. 2023-06-07 12:15:14 +02:00
Juan Carlos Garrote
5607f76a1d
Merge pull request #566 from owncloud/technical/min_sdk_23
[TECHNICAL] Upgrade min SDK to Android 6 (v23)
2023-05-25 14:03:37 +02:00
Juan Carlos Garrote
b4137502d2 Remove unnecessary code after upgrading to min SDK 23 2023-05-25 13:33:49 +02:00
Juan Carlos Garrote
261075a8ad Update minSdkVersion to 23 (Android 6) 2023-05-25 13:33:49 +02:00
Juan Carlos Garrote
c2874357f0
Merge pull request #569 from owncloud/release/4.0
[Release] 2.1
2023-05-25 13:30:31 +02:00
Juan Carlos Garrote
e6937b4210 Removed publicUpload parameter from public shares creation and update requests 2023-05-15 14:01:16 +02:00
Juan Carlos Garrote
89dd13cec4
Merge pull request #562 from owncloud/feature/create_file_web
[FEATURE REQUEST] Create new file via Open in Web
2023-05-04 09:11:24 +02:00
Juan Carlos Garrote
ab3a594e5c Create network call to create file via app provider 2023-04-14 11:02:21 +02:00
Juan Carlos Garrote
cbbe044212
Merge pull request #558 from owncloud/fix/unknown_error_message
[FIX] Introduce new error message for ProtocolException
2023-04-13 08:26:42 +02:00
Juan Carlos Garrote
17aa1ea7bd Introduce new error message for ProtocolException 2023-04-13 08:20:50 +02:00
Abel García de Prada
cbf6365e93
Merge pull request #559 from owncloud/dependabot/gradle/org.robolectric-robolectric-4.10
Bump org.robolectric:robolectric from 4.9.2 to 4.10
2023-04-12 08:21:19 +02:00
dependabot[bot]
15bccd3f80
Bump org.robolectric:robolectric from 4.9.2 to 4.10
Bumps [org.robolectric:robolectric](https://github.com/robolectric/robolectric) from 4.9.2 to 4.10.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.9.2...robolectric-4.10)

---
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>
2023-04-12 02:57:14 +00:00
Juan Carlos Garrote
8cee0a37a7
Merge pull request #553 from owncloud/feature/app_registry
[Feature] Open in specific web provider
2023-04-10 09:17:36 +02:00
Abel García de Prada
393ec6e07e Open file in web with a specific app 2023-04-05 16:16:50 +02:00
Abel García de Prada
f35daacdf1 Pleasure the lint 2023-04-05 16:16:50 +02:00
Abel García de Prada
2ac5cf0657 Move open in web operations to their proper location 2023-04-05 16:16:50 +02:00
Abel García de Prada
173b12eeca Retrieve the list of available apps to open in web 2023-04-05 16:16:50 +02:00
Juan Carlos Garrote
602ab41fcc
Merge pull request #555 from owncloud/feature/auth_webfinger_flow
Update WebFinger flow
2023-04-04 13:44:31 +02:00
Juan Carlos Garrote
3a27baad3a Fix WebfingerResponse tests 2023-04-04 11:51:06 +02:00
Abel García de Prada
14baadd7ea Webfinger calls won't follow redirections. Only working with 2XX 2023-03-31 09:41:57 +02:00
Abel García de Prada
fa65c1a84d
Merge pull request #551 from owncloud/feature/add_language_header
Add accept language header to all requests
2023-03-24 13:33:42 +01:00
Abel García de Prada
c429b575a9 Add accept language header to all requests 2023-03-24 08:31:34 +01:00
Abel García de Prada
f7d4d27ebb
Merge pull request #552 from owncloud/remove_drives_permission_parsing
Remove permission parsing from spaces.
2023-03-23 10:21:01 +01:00
Abel García de Prada
3681f1001a Remove permission parsing from spaces. Will be done via WebDav permissions 2023-03-22 15:23:57 +01:00
Abel García de Prada
ee5130d3e6
Merge pull request #550 from owncloud/technical/bump_target_sdk
[TECHNICAL] Bump target sdk to 33
2023-03-21 12:23:38 +01:00
Abel García de Prada
1f93cfaf52 Bump target sdk to 33 2023-03-16 13:06:00 +01:00
Juan Carlos Garrote
e6e30fc352
Merge pull request #549 from owncloud/release/2.1
[Release] 2.1 beta.1
2023-03-13 13:09:04 +01:00
Abel García de Prada
c53475da37 Removing quotas from webdav properties in regular propfinds 2023-03-10 12:47:35 +01:00
Abel García de Prada
c2f32b2a82
Merge pull request #548 from owncloud/technical/replace_kapt_with_ksp
[Technical] Replace kapt with ksp
2023-03-09 13:02:47 +01:00
Abel García de Prada
6846e25fa3 Replace kapt with ksp 2023-03-09 11:51:13 +01:00
Juan Carlos Garrote
0e9f9c6391
Merge pull request #530 from owncloud/spaces/main
[SPACES] Main features
2023-03-09 11:49:00 +01:00
Juan Carlos Garrote
159dcd6d68 Rename file to be the same as the class it contains 2023-03-09 09:43:07 +01:00
Juan Carlos Garrote
b660ee98fd Rename files to be the same as the classes they contain 2023-03-09 09:37:09 +01:00
Abel García de Prada
ff90598a2d Rename WebFinger classes to make them consistent 2023-03-09 09:19:25 +01:00
Abel García de Prada
0017079a69 Allow retrieval of several instances from webfinger 2023-03-09 09:19:25 +01:00
Juan Carlos Garrote
2458db1828 Fix to KtLint report 2023-03-09 09:19:25 +01:00
Juan Carlos Garrote
e6f3fd2e16 Upload workers and network operations adapted to spaces 2023-03-09 09:19:25 +01:00
Juan Carlos Garrote
a395787e0b Adapted copy operation for spaces 2023-03-09 09:19:25 +01:00
Juan Carlos Garrote
e9f291371d Move network operation adapted to spaces 2023-03-09 09:19:25 +01:00
Abel García de Prada
b65efc2b69 Allow downloads from specific WebDav urls 2023-03-09 09:19:25 +01:00
Abel García de Prada
78665e8cb0 Add support for spaces web dav specific urls to the rename operation 2023-03-09 09:19:25 +01:00
Abel García de Prada
9c844aae7e Support removal of files from specific space 2023-03-09 09:19:25 +01:00
Abel García de Prada
2c18e7b679 Added create folder operation support for specific space 2023-03-09 09:19:25 +01:00
Abel García de Prada
cb73d537e4 Allow support to read specific file from a space 2023-03-09 09:19:25 +01:00
Abel García de Prada
8e4f243031 Fix remote path retrieval. Now it depends on webdav url to support spaces 2023-03-09 09:19:25 +01:00
Abel García de Prada
a65a82cae0 Adapt the propfind to work with specific webdavurl from the space 2023-03-09 09:19:25 +01:00
Abel García de Prada
45f53656ba Update permissions parsing to latest api changes
Supports api renaming from grantedTo to grantedToIdentities on v1.0.1
https://github.com/owncloud/libre-graph-api/releases/tag/v1.0.1
2023-03-09 09:19:25 +01:00
Abel García de Prada
8a4fcb6550 Support shares space 2023-03-09 09:19:25 +01:00
Abel García de Prada
31ccf56e97 Fix parsing when space is disabled 2023-03-09 09:19:25 +01:00
Juan Carlos Garrote
0d7a49b3ff Some type changes and renamings 2023-03-09 09:19:25 +01:00
Abel García de Prada
7bb94cf289 Make quota attribute nullable. Its not mandatory 2023-03-09 09:19:25 +01:00
Abel García de Prada
b1a3402656 Create spaces fetch operation and spaces service 2023-03-09 09:19:25 +01:00
Juan Carlos Garrote
f0dda9eb8b Added trailing comma 2023-03-09 09:19:25 +01:00
Juan Carlos Garrote
4f001550f9 Save spaces-related capabilities in database 2023-03-09 09:19:25 +01:00
Juan Carlos Garrote
e30246c486
Merge pull request #547 from owncloud/release/2.0.3
[Release] 2.0.3
2023-03-07 18:20:51 +01:00
Abel García de Prada
602b7e7548 Show the url in the response http log too 2023-03-07 14:14:54 +01:00
Abel García de Prada
f8da84d0ad
Merge pull request #544 from owncloud/dependabot/gradle/com.android.tools.build-gradle-7.4.2
Bump com.android.tools.build:gradle from 7.4.1 to 7.4.2
2023-02-28 08:02:23 +01:00
dependabot[bot]
763005b051
Bump com.android.tools.build:gradle from 7.4.1 to 7.4.2
Bumps com.android.tools.build:gradle from 7.4.1 to 7.4.2.

---
updated-dependencies:
- dependency-name: com.android.tools.build:gradle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 02:57:18 +00:00
Abel García de Prada
d6f969bd91
Merge pull request #540 from owncloud/bump/kotlin_gradle
Bump kotlin version to 1.8.10
2023-02-14 10:17:37 +01:00
Abel García de Prada
4200ed324c Bump kotlin version to 1.8.10 2023-02-14 09:45:29 +01:00
Abel García de Prada
7ee2c5eb15
Merge pull request #535 from owncloud/dependabot/gradle/org.jlleitschuh.gradle-ktlint-gradle-11.1.0
Bump ktlint-gradle from 11.0.0 to 11.1.0
2023-01-30 10:47:27 +01:00
dependabot[bot]
b5cd4a1df2
Bump ktlint-gradle from 11.0.0 to 11.1.0
Bumps ktlint-gradle from 11.0.0 to 11.1.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>
2023-01-30 02:11:34 +00:00
Abel García de Prada
8a8a931e66
Merge pull request #534 from owncloud/release/2.0.2
[Release] 2.0.2
2023-01-27 13:11:39 +01:00
Abel García de Prada
148a97cd32 Potential fix to oauth error after logging in for first time that makes user to reauthenticate 2023-01-26 14:36:38 +01:00
Abel García de Prada
00948ffd73
Merge pull request #528 from owncloud/dependabot/gradle/org.robolectric-robolectric-4.9.2
Bump robolectric from 4.9.1 to 4.9.2
2023-01-09 08:34:18 +01:00
dependabot[bot]
118646290d
Bump robolectric from 4.9.1 to 4.9.2
Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.9.1 to 4.9.2.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.9.1...robolectric-4.9.2)

---
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-12-28 02:01:23 +00:00
Abel García de Prada
defa4d1469
Merge pull request #525 from owncloud/debug/connection_validator_logs
Add several logs to try to debug potential errors related to token refreshment
2022-12-21 12:15:35 +01:00
Abel García de Prada
3545686a31 Add several logs to try to debug potential errors related to oAuth 2022-12-21 11:32:55 +01:00
Abel García de Prada
b710070426
Merge pull request #524 from owncloud/dependabot/gradle/org.robolectric-robolectric-4.9.1
Bump robolectric from 4.9 to 4.9.1
2022-12-20 12:58:27 +01:00
dependabot[bot]
a0750c613a
Bump robolectric from 4.9 to 4.9.1
Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.9 to 4.9.1.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.9...robolectric-4.9.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-12-20 02:01:28 +00:00
Abel García de Prada
9df62a51f3
Merge pull request #523 from owncloud/fix/unshare_ocis
Unsharing wont return anything anymore since result object was not used
2022-12-19 08:29:11 +01:00
Abel García de Prada
fa7bfdd597 Unsharing wont return anything anymore since result object was not used 2022-12-15 15:26:37 +01:00
Abel García de Prada
cb076aa6b9
Merge pull request #520 from owncloud/dependencies/bump_bulk
Bump several dependencies at the same time
2022-12-15 14:49:39 +01:00
Abel García de Prada
7ceef58382 Bump gradle plugin to 7.3.1 and move package name fro manifest to gradle namespace 2022-12-13 19:00:10 +01:00
Abel García de Prada
a877612fca Bump several dependencies at the same time 2022-12-13 14:51:57 +01:00
Abel García de Prada
359e591275
Merge pull request #339 from owncloud/new_arch/synchronization
[New arch] Synchronization
2022-12-12 09:54:27 +01:00
56 changed files with 1108 additions and 325 deletions

View File

@ -49,6 +49,6 @@ ownCloud Android Library uses OkHttp version 4.6.0, licensed under Apache Licens
### Compatibility
ownCloud Android Library is valid for Android systems from version Android 5 (android:minSdkVersion="21" android:targetSdkVersion="29").
ownCloud Android Library is valid for Android systems from version Android 6 (android:minSdkVersion="23" android:targetSdkVersion="33").
ownCloud Android library supports ownCloud server from version 4.5.

View File

@ -1,7 +1,7 @@
buildscript {
ext {
kotlinVersion = '1.6.21'
moshiVersion = "1.13.0"
orgJetbrainsKotlin = '1.8.10'
comSquareupMoshi = '1.14.0'
}
repositories {
@ -10,12 +10,16 @@ buildscript {
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
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"
classpath "org.jlleitschuh.gradle:ktlint-gradle:11.1.0"
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$orgJetbrainsKotlin"
}
}
plugins {
id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false
}
allprojects {
repositories {
google()
@ -26,4 +30,5 @@ allprojects {
subprojects {
apply plugin: "org.jlleitschuh.gradle.ktlint"
apply plugin: "com.google.devtools.ksp"
}

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1,32 +1,32 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.devtools.ksp'
apply plugin: 'kotlin-parcelize'
dependencies {
api 'com.squareup.okhttp3:okhttp:4.6.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$orgJetbrainsKotlin"
api 'com.gitlab.ownclouders:dav4android:oc_support_2.1.5'
api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2'
// Moshi
implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") {
implementation("com.squareup.moshi:moshi-kotlin:$comSquareupMoshi") {
exclude module: "kotlin-reflect"
}
implementation 'org.apache.commons:commons-lang3:3.12.0'
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
ksp "com.squareup.moshi:moshi-kotlin-codegen:$comSquareupMoshi"
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.9'
testImplementation 'org.robolectric:robolectric:4.10'
debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.6.0'
}
android {
compileSdkVersion 31
compileSdkVersion 33
defaultConfig {
minSdkVersion 21
targetSdkVersion 31
minSdkVersion 23
targetSdkVersion 33
}
lint {
@ -39,4 +39,5 @@ android {
includeAndroidResources = true
}
}
namespace 'com.owncloud.android.lib'
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2016 ownCloud GmbH.
Copyright (C) 2023 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
@ -23,16 +23,8 @@
-->
<manifest package="com.owncloud.android.lib"
xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- USE_CREDENTIALS, MANAGE_ACCOUNTS and AUTHENTICATE_ACCOUNTS are needed for API < 23.
In API >= 23 the do not exist anymore -->
<uses-permission
android:name="android.permission.MANAGE_ACCOUNTS"
android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

View File

@ -37,7 +37,6 @@ 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
@ -46,7 +45,7 @@ import java.lang.Exception
*/
class ConnectionValidator(
val context: Context,
val clearCookiesOnValidation: Boolean
private val clearCookiesOnValidation: Boolean
) {
fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager, context: Context): Boolean {
try {
@ -61,12 +60,12 @@ class ConnectionValidator(
client.account = baseClient.account
client.credentials = baseClient.credentials
while (validationRetryCount < VALIDATION_RETRY_COUNT) {
Timber.d("validationRetryCout %d", validationRetryCount)
Timber.d("validationRetryCount %d", validationRetryCount)
var successCounter = 0
var failCounter = 0
client.setFollowRedirects(true)
if (isOnwCloudStatusOk(client)) {
if (isOwnCloudStatusOk(client)) {
successCounter++
} else {
failCounter++
@ -103,7 +102,7 @@ class ConnectionValidator(
return false
}
private fun isOnwCloudStatusOk(client: OwnCloudClient): Boolean {
private fun isOwnCloudStatusOk(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
@ -138,6 +137,12 @@ class ConnectionValidator(
// test if have all the needed to effectively invalidate ...
shouldInvalidateAccountCredentials =
shouldInvalidateAccountCredentials and (account.savedAccount != null)
Timber.d(
"""Received error: $httpStatusCode,
account: ${account.name}
credentials are real: ${credentials !is OwnCloudAnonymousCredentials},
so we need to invalidate credentials for account ${account.name} : $shouldInvalidateAccountCredentials"""
)
return shouldInvalidateAccountCredentials
}
@ -150,6 +155,7 @@ class ConnectionValidator(
*
*/
private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) {
Timber.i("Invalidating account credentials for account $account")
val am = AccountManager.get(context)
am.invalidateAuthToken(
account.savedAccount.type,
@ -167,8 +173,6 @@ class ConnectionValidator(
*
* Refresh current credentials if possible, and marks a retry.
*
* @param status
* @param repeatCounter
* @return
*/
private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean {
@ -181,6 +185,7 @@ class ConnectionValidator(
if (credentials.authTokenCanBeRefreshed()) {
try {
// This command does the actual refresh
Timber.i("Trying to refresh auth token for account $account")
account.loadCredentials(context)
// if mAccount.getCredentials().length() == 0 --> refresh failed
client.credentials = account.credentials
@ -201,6 +206,7 @@ class ConnectionValidator(
if (!credentialsWereRefreshed) {
// if credentials are not refreshed, client must be removed
// from the OwnCloudClientManager to prevent it is reused once and again
Timber.w("Credentials were not refreshed, client will be removed from the Session Manager to prevent using it over and over")
singleSessionManager.removeClientFor(account)
}
}
@ -210,6 +216,6 @@ class ConnectionValidator(
}
companion object {
private val VALIDATION_RETRY_COUNT = 3
private const val VALIDATION_RETRY_COUNT = 3
}
}

View File

@ -43,6 +43,7 @@ import timber.log.Timber;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Locale;
import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER;
import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY;
@ -128,6 +129,7 @@ public class OwnCloudClient extends HttpClient {
Timber.d("Executing in request with id %s", requestId);
method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId);
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
method.setRequestHeader(HttpConstants.ACCEPT_LANGUAGE_HEADER, Locale.getDefault().getLanguage());
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
if (mCredentials.getHeaderAuth() != null && !mCredentials.getHeaderAuth().isEmpty()) {
method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());

View File

@ -31,7 +31,6 @@ import android.net.Uri;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.http.HttpClient;
import timber.log.Timber;
import java.io.IOException;
@ -124,6 +123,24 @@ public class SingleSessionManager {
}
} else {
Timber.v("reusing client for account %s", accountName);
if (client.getAccount() != null &&
client.getAccount().getCredentials() != null &&
(client.getAccount().getCredentials().getAuthToken() == null || client.getAccount().getCredentials().getAuthToken().isEmpty())
) {
Timber.i("Client " + client.getAccount().getName() + " needs to refresh credentials");
//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
client.clearCookies();
client.clearCredentials();
client.setAccount(account);
account.loadCredentials(context);
client.setCredentials(account.getCredentials());
Timber.i("Client " + account.getName() + " with credentials size" + client.getAccount().getCredentials().getAuthToken().length());
}
reusingKnown = true;
}

View File

@ -112,6 +112,7 @@ public class AccountUtils {
String username = AccountUtils.getUsernameForAccount(account);
if (isOauth2) {
Timber.i("Trying to retrieve credentials for oAuth account" + account.name);
String accessToken = am.blockingGetAuthToken(
account,
AccountTypeUtils.getAuthTokenTypeAccessToken(account.type),

View File

@ -40,6 +40,7 @@ public class HttpConstants {
public static final String IF_MATCH_HEADER = "If-Match";
public static final String IF_NONE_MATCH_HEADER = "If-None-Match";
public static final String CONTENT_TYPE_HEADER = "Content-Type";
public static final String ACCEPT_LANGUAGE_HEADER = "Accept-Language";
public static final String CONTENT_LENGTH_HEADER = "Content-Length";
public static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length";
public static final String OC_X_OC_MTIME_HEADER = "X-OC-Mtime";

View File

@ -51,7 +51,7 @@ class LogInterceptor : Interceptor {
val request = chain.request().also {
val requestId = it.headers[OC_X_REQUEST_ID]
logHttp(REQUEST, INFO, requestId, "Type: ${it.method} URL: ${it.url}")
logHttp(REQUEST, INFO, requestId, "Method: ${it.method} URL: ${it.url}")
logHeaders(requestId, it.headers, REQUEST)
logRequestBody(requestId, it.body)
}
@ -64,7 +64,7 @@ class LogInterceptor : Interceptor {
RESPONSE,
INFO,
requestId,
"Code: ${it.code} Message: ${it.message} IsSuccessful: ${it.isSuccessful}"
"Method: ${request.method} URL: ${request.url} Code: ${it.code} Message: ${it.message}"
)
logHeaders(requestId, it.headers, RESPONSE)
logResponseBody(requestId, it.body)

View File

@ -36,7 +36,7 @@ import java.net.URL
class CopyMethod(
val url: URL,
private val destinationUrl: String,
private val forceOverride: Boolean = false
val forceOverride: Boolean = false
) : DavMethod(url) {
@Throws(Exception::class)
public override fun onDavExecute(davResource: DavOCResource): Int {

View File

@ -24,13 +24,36 @@
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 at.bitfire.dav4jvm.property.CreationDate
import at.bitfire.dav4jvm.property.DisplayName
import at.bitfire.dav4jvm.property.GetContentLength
import at.bitfire.dav4jvm.property.GetContentType
import at.bitfire.dav4jvm.property.GetETag
import at.bitfire.dav4jvm.property.GetLastModified
import at.bitfire.dav4jvm.property.OCId
import at.bitfire.dav4jvm.property.OCPermissions
import at.bitfire.dav4jvm.property.OCPrivatelink
import at.bitfire.dav4jvm.property.OCSize
import at.bitfire.dav4jvm.property.ResourceType
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
object DavUtils {
@JvmStatic val allPropset: Array<Property.Name>
get() = getAllPropSet().plus(OCShareTypes.NAME)
@JvmStatic val allPropSet: Array<Property.Name>
get() = arrayOf(
DisplayName.NAME,
GetContentType.NAME,
ResourceType.NAME,
GetContentLength.NAME,
GetLastModified.NAME,
CreationDate.NAME,
GetETag.NAME,
OCPermissions.NAME,
OCId.NAME,
OCSize.NAME,
OCPrivatelink.NAME,
OCShareTypes.NAME,
)
val quotaPropSet: Array<Property.Name>
get() = getQuotaPropset()

View File

@ -36,7 +36,7 @@ import java.net.URL
class MoveMethod(
url: URL,
private val destinationUrl: String,
private val forceOverride: Boolean = false
val forceOverride: Boolean = false
) : DavMethod(url) {
@Throws(Exception::class)
override fun onDavExecute(davResource: DavOCResource): Int {

View File

@ -46,6 +46,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
@ -166,7 +167,10 @@ public class RemoteOperationResult<T>
} else if (e instanceof FileNotFoundException) {
mCode = ResultCode.LOCAL_FILE_NOT_FOUND;
} else {
} else if (e instanceof ProtocolException) {
mCode = ResultCode.NETWORK_ERROR;
}
else {
mCode = ResultCode.UNKNOWN_ERROR;
}
}
@ -589,5 +593,6 @@ public class RemoteOperationResult<T>
SPECIFIC_METHOD_NOT_ALLOWED,
SPECIFIC_BAD_REQUEST,
TOO_EARLY,
NETWORK_ERROR,
}
}

View File

@ -0,0 +1,108 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 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.appregistry
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.squareup.moshi.Json
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 CreateRemoteFileWithAppProviderOperation(
private val createFileWithAppProviderEndpoint: String,
private val parentContainerId: String,
private val filename: String,
) : RemoteOperation<String>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
return try {
val createFileWithAppProviderRequestBody = CreateFileWithAppProviderParams(parentContainerId, filename)
.toRequestBody()
val stringUrl = client.baseUri.toString() + WebdavUtils.encodePath(createFileWithAppProviderEndpoint)
val postMethod = PostMethod(URL(stringUrl), createFileWithAppProviderRequestBody).apply {
setReadTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
setConnectionTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
}
val status = client.executeHttpMethod(postMethod)
Timber.d("Create file $filename with app provider in folder with ID $parentContainerId - $status${if (!isSuccess(status)) "(FAIL)" else ""}")
if (isSuccess(status)) RemoteOperationResult<String>(ResultCode.OK).apply {
val moshi = Moshi.Builder().build()
val adapter: JsonAdapter<CreateFileWithAppProviderResponse> = moshi.adapter(CreateFileWithAppProviderResponse::class.java)
data = postMethod.getResponseBodyAsString()?.let { adapter.fromJson(it)!!.fileId }
}
else RemoteOperationResult<String>(postMethod).apply { data = "" }
} catch (e: Exception) {
val result = RemoteOperationResult<String>(e)
Timber.e(e, "Create file $filename with app provider in folder with ID $parentContainerId failed")
result
}
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
data class CreateFileWithAppProviderParams(
val parentContainerId: String,
val filename: String,
) {
fun toRequestBody(): RequestBody =
FormBody.Builder()
.add(PARAM_PARENT_CONTAINER_ID, parentContainerId)
.add(PARAM_FILENAME, filename)
.build()
companion object {
const val PARAM_PARENT_CONTAINER_ID = "parent_container_id"
const val PARAM_FILENAME = "filename"
}
}
@JsonClass(generateAdapter = true)
data class CreateFileWithAppProviderResponse(
@Json(name = "file_id")
val fileId: String,
)
companion object {
private const val TIMEOUT: Long = 5_000
}
}

View File

@ -0,0 +1,78 @@
/* ownCloud Android Library is available under MIT license
* @author Abel García de Prada
*
* Copyright (C) 2023 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.appregistry
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
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.OK
import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import timber.log.Timber
import java.net.URL
class GetRemoteAppRegistryOperation(private val appUrl: String?) : RemoteOperation<AppRegistryResponse>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<AppRegistryResponse> {
var result: RemoteOperationResult<AppRegistryResponse>
try {
val uriBuilder = client.baseUri.buildUpon().apply {
appendEncodedPath(appUrl)
}
val getMethod = GetMethod(URL(uriBuilder.build().toString()))
val status = client.executeHttpMethod(getMethod)
val response = getMethod.getResponseBodyAsString()
if (status == HttpConstants.HTTP_OK) {
Timber.d("Successful response $response")
// Parse the response
val moshi: Moshi = Moshi.Builder().build()
val adapter: JsonAdapter<AppRegistryResponse> = moshi.adapter(AppRegistryResponse::class.java)
val appRegistryResponse: AppRegistryResponse = response?.let { adapter.fromJson(it) } ?: AppRegistryResponse(value = emptyList())
result = RemoteOperationResult(OK)
result.data = appRegistryResponse
Timber.d("Get AppRegistry completed and parsed to ${result.data}")
} else {
result = RemoteOperationResult(getMethod)
Timber.e("Failed response while getting app registry from the server status code: $status; response message: $response")
}
} catch (e: Exception) {
result = RemoteOperationResult(e)
Timber.e(e, "Exception while getting app registry")
}
return result
}
}

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
* Copyright (C) 2023 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
@ -21,7 +21,8 @@
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
package com.owncloud.android.lib.resources.appregistry
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
@ -30,7 +31,6 @@ 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
@ -41,16 +41,18 @@ import java.net.URL
import java.util.concurrent.TimeUnit
class GetUrlToOpenInWebRemoteOperation(
val openWithWebEndpoint: String,
val fileId: String,
private val openWithWebEndpoint: String,
private val fileId: String,
private val appName: String,
) : RemoteOperation<String>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
return try {
val openInWebRequestBody = OpenInWebParams(fileId).toRequestBody()
val openInWebRequestBody = OpenInWebParams(fileId, appName).toRequestBody()
val stringUrl = client.baseUri.toString() + WebdavUtils.encodePath(openWithWebEndpoint) + "?$PARAM_FILE_ID=$fileId"
val stringUrl =
client.baseUri.toString() + WebdavUtils.encodePath(openWithWebEndpoint)
val postMethod = PostMethod(URL(stringUrl), openInWebRequestBody).apply {
setReadTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
@ -75,14 +77,21 @@ class GetUrlToOpenInWebRemoteOperation(
}
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
data class OpenInWebParams(val fileId: String) {
data class OpenInWebParams(
val fileId: String,
val appName: String,
) {
fun toRequestBody(): RequestBody =
FormBody.Builder().build()
FormBody.Builder()
.add(PARAM_FILE_ID, fileId)
.add(PARAM_APP_NAME, appName)
.build()
companion object {
const val PARAM_FILE_ID = "file_id"
const val PARAM_APP_NAME = "app_name"
}
}

View File

@ -0,0 +1,56 @@
/* ownCloud Android Library is available under MIT license
* @author Abel García de Prada
*
* Copyright (C) 2023 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.appregistry.responses
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class AppRegistryResponse(
@Json(name = "mime-types")
val value: List<AppRegistryMimeTypeResponse>
)
@JsonClass(generateAdapter = true)
data class AppRegistryMimeTypeResponse(
@Json(name = "mime_type") val mimeType: String,
val ext: String? = null,
@Json(name = "app_providers")
val appProviders: List<AppRegistryProviderResponse>,
val name: String? = null,
val icon: String? = null,
val description: String? = null,
@Json(name = "allow_creation")
val allowCreation: Boolean? = null,
@Json(name = "default_application")
val defaultApplication: String? = null
)
@JsonClass(generateAdapter = true)
data class AppRegistryProviderResponse(
val name: String,
val icon: String,
)

View File

@ -0,0 +1,44 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 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.appregistry.services
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.Service
import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse
interface AppRegistryService : Service {
fun getAppRegistry(appUrl: String?): RemoteOperationResult<AppRegistryResponse>
fun getUrlToOpenInWeb(
openWebEndpoint: String,
fileId: String,
appName: String,
): RemoteOperationResult<String>
fun createFileWithAppProvider(
createFileWithAppProviderEndpoint: String,
parentContainerId: String,
filename: String,
): RemoteOperationResult<String>
}

View File

@ -0,0 +1,54 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 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.appregistry.services
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.appregistry.CreateRemoteFileWithAppProviderOperation
import com.owncloud.android.lib.resources.appregistry.GetRemoteAppRegistryOperation
import com.owncloud.android.lib.resources.appregistry.GetUrlToOpenInWebRemoteOperation
import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse
class OCAppRegistryService(override val client: OwnCloudClient) : AppRegistryService {
override fun getAppRegistry(appUrl: String?): RemoteOperationResult<AppRegistryResponse> =
GetRemoteAppRegistryOperation(appUrl).execute(client)
override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String, appName: String): RemoteOperationResult<String> =
GetUrlToOpenInWebRemoteOperation(
openWithWebEndpoint = openWebEndpoint,
fileId = fileId,
appName = appName
).execute(client)
override fun createFileWithAppProvider(
createFileWithAppProviderEndpoint: String,
parentContainerId: String,
filename: String
): RemoteOperationResult<String> =
CreateRemoteFileWithAppProviderOperation(
createFileWithAppProviderEndpoint = createFileWithAppProviderEndpoint,
parentContainerId = parentContainerId,
filename = filename,
).execute(client)
}

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
* Copyright (C) 2023 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
@ -25,7 +25,7 @@ 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.allPropset
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils.allPropSet
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
@ -41,6 +41,7 @@ import java.util.concurrent.TimeUnit
* @author David A. Velasco
* @author David González Verdugo
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
*
* @param remotePath Path to append to the URL owned by the client instance.
* @param isUserLoggedIn When `true`, the username won't be added at the end of the PROPFIND url since is not
@ -48,21 +49,21 @@ import java.util.concurrent.TimeUnit
*/
class CheckPathExistenceRemoteOperation(
val remotePath: String? = "",
val isUserLoggedIn: Boolean
val isUserLoggedIn: Boolean,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<Boolean>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> {
return try {
val stringUrl =
if (isUserLoggedIn) client.baseFilesWebDavUri.toString()
else client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)
val baseStringUrl = spaceWebDavUrl ?: if (isUserLoggedIn) client.userFilesWebDavUri.toString() else client.baseFilesWebDavUri.toString()
val stringUrl = if (isUserLoggedIn) baseStringUrl + WebdavUtils.encodePath(remotePath) else baseStringUrl
val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropset).apply {
return try {
val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropSet).apply {
setReadTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
}
var status = client.executeHttpMethod(propFindMethod)
val status = client.executeHttpMethod(propFindMethod)
/* PROPFIND method
* 404 NOT FOUND: path doesn't exist,
* 207 MULTI_STATUS: path exists.
@ -77,8 +78,9 @@ class CheckPathExistenceRemoteOperation(
val result = RemoteOperationResult<Boolean>(e)
Timber.e(
e,
"Existence check for ${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)} : ${result.logMessage}"
"Existence check for $stringUrl : ${result.logMessage}"
)
result.data = false
result
}
}

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
* Copyright (C) 2023 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
@ -44,25 +44,31 @@ import java.util.concurrent.TimeUnit
* @author David A. Velasco
* @author Christian Schabesberger
* @author David González V.
* @author Juan Carlos Garrote Gascón
* @author Manuel Plazas Palacio
*
* @param srcRemotePath Remote path of the file/folder to copy.
* @param sourceRemotePath Remote path of the file/folder to copy.
* @param targetRemotePath Remote path desired for the file/folder to copy it.
*/
class CopyRemoteFileOperation(
private val srcRemotePath: String,
private val sourceRemotePath: String,
private val targetRemotePath: String,
private val sourceSpaceWebDavUrl: String? = null,
private val targetSpaceWebDavUrl: String? = null,
private val forceOverride: Boolean = false,
) : RemoteOperation<String>() {
/**
* Performs the rename operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
if (targetRemotePath == srcRemotePath) {
if (targetRemotePath == sourceRemotePath && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) {
// nothing to do!
return RemoteOperationResult(ResultCode.OK)
}
if (targetRemotePath.startsWith(srcRemotePath)) {
if (targetRemotePath.startsWith(sourceRemotePath) && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) {
return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT)
}
@ -70,9 +76,11 @@ class CopyRemoteFileOperation(
var result: RemoteOperationResult<String>
try {
val copyMethod = CopyMethod(
URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(srcRemotePath)),
client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath),
url = URL((sourceSpaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(sourceRemotePath)),
destinationUrl = (targetSpaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(targetRemotePath),
forceOverride = forceOverride,
).apply {
addRequestHeaders(this)
setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS)
setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
}
@ -83,6 +91,7 @@ class CopyRemoteFileOperation(
result = RemoteOperationResult(ResultCode.OK)
result.setData(fileRemoteId)
}
isPreconditionFailed(status) -> {
result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE)
client.exhaustResponse(copyMethod.getResponseBodyAsStream())
@ -90,19 +99,27 @@ class CopyRemoteFileOperation(
/// for other errors that could be explicitly handled, check first:
/// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
}
else -> {
result = RemoteOperationResult(copyMethod)
client.exhaustResponse(copyMethod.getResponseBodyAsStream())
}
}
Timber.i("Copy $srcRemotePath to $targetRemotePath: ${result.logMessage}")
Timber.i("Copy $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
} catch (e: Exception) {
result = RemoteOperationResult(e)
Timber.e(e, "Copy $srcRemotePath to $targetRemotePath: ${result.logMessage}")
Timber.e(e, "Copy $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
}
return result
}
private fun addRequestHeaders(copyMethod: CopyMethod) {
//Adding this because the library has an error with override
if (copyMethod.forceOverride) {
copyMethod.setRequestHeader(OVERWRITE, TRUE)
}
}
private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
private fun isPreconditionFailed(status: Int) = status == HttpConstants.HTTP_PRECONDITION_FAILED
@ -110,5 +127,7 @@ class CopyRemoteFileOperation(
companion object {
private const val COPY_READ_TIMEOUT = 10L
private const val COPY_CONNECTION_TIMEOUT = 6L
private const val OVERWRITE = "overwrite"
private const val TRUE = "T"
}
}

View File

@ -46,7 +46,8 @@ import java.util.concurrent.TimeUnit
class CreateRemoteFolderOperation(
val remotePath: String,
private val createFullPath: Boolean,
private val isChunksFolder: Boolean = false
private val isChunksFolder: Boolean = false,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<Unit>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
@ -67,13 +68,13 @@ class CreateRemoteFolderOperation(
var result: RemoteOperationResult<Unit>
try {
val webDavUri = if (isChunksFolder) {
client.uploadsWebDavUri
client.uploadsWebDavUri.toString()
} else {
client.userFilesWebDavUri
spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
}
val mkCol = MkColMethod(
URL(webDavUri.toString() + WebdavUtils.encodePath(remotePath))
URL(webDavUri + WebdavUtils.encodePath(remotePath))
).apply {
setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)

View File

@ -36,7 +36,6 @@ import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.net.URL
import java.util.HashSet
import java.util.concurrent.atomic.AtomicBoolean
/**
@ -47,7 +46,8 @@ import java.util.concurrent.atomic.AtomicBoolean
*/
class DownloadRemoteFileOperation(
private val remotePath: String,
localFolderPath: String
localFolderPath: String,
private val spaceWebDavUrl: String? = null,
) : RemoteOperation<Unit>() {
private val cancellationRequested = AtomicBoolean(false)
@ -84,7 +84,8 @@ class DownloadRemoteFileOperation(
var bis: BufferedInputStream? = null
var savedFile = false
val getMethod = GetMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)))
val webDavUri = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
val getMethod = GetMethod(URL(webDavUri + WebdavUtils.encodePath(remotePath)))
try {
val status = client.executeHttpMethod(getMethod)

View File

@ -44,7 +44,7 @@ class GetBaseUrlRemoteOperation : RemoteOperation<String?>() {
return try {
val stringUrl = client.baseFilesWebDavUri.toString()
val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropset).apply {
val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropSet).apply {
setReadTimeout(TIMEOUT, TimeUnit.SECONDS)
setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS)
}

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2021 ownCloud GmbH.
* Copyright (C) 2023 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
@ -38,17 +38,24 @@ import java.util.concurrent.TimeUnit
/**
* Remote operation moving a remote file or folder in the ownCloud server to a different folder
* in the same account.
* in the same account and space.
*
* Allows renaming the moving file/folder at the same time.
*
* @author David A. Velasco
* @author David González Verdugo
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
* @author Manuel Plazas Palacio
*
* @param sourceRemotePath Remote path of the file/folder to copy.
* @param targetRemotePath Remote path desired for the file/folder to copy it.
*/
open class MoveRemoteFileOperation(
private val sourceRemotePath: String,
private val targetRemotePath: String,
private val spaceWebDavUrl: String? = null,
private val forceOverride: Boolean = false,
) : RemoteOperation<Unit>() {
/**
@ -73,8 +80,9 @@ open class MoveRemoteFileOperation(
// so this uri has to be customizable
val srcWebDavUri = getSrcWebDavUriForClient(client)
val moveMethod = MoveMethod(
url = URL(srcWebDavUri.toString() + WebdavUtils.encodePath(sourceRemotePath)),
destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath),
url = URL((spaceWebDavUrl ?: srcWebDavUri.toString()) + WebdavUtils.encodePath(sourceRemotePath)),
destinationUrl = (spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(targetRemotePath),
forceOverride = forceOverride,
).apply {
addRequestHeaders(this)
setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS)
@ -87,6 +95,7 @@ open class MoveRemoteFileOperation(
isSuccess(status) -> {
result = RemoteOperationResult<Unit>(ResultCode.OK)
}
isPreconditionFailed(status) -> {
result = RemoteOperationResult<Unit>(ResultCode.INVALID_OVERWRITE)
client.exhaustResponse(moveMethod.getResponseBodyAsStream())
@ -94,6 +103,7 @@ open class MoveRemoteFileOperation(
/// for other errors that could be explicitly handled, check first:
/// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
}
else -> {
result = RemoteOperationResult<Unit>(moveMethod)
client.exhaustResponse(moveMethod.getResponseBodyAsStream())
@ -120,6 +130,10 @@ open class MoveRemoteFileOperation(
* In case new headers are needed, override this method
*/
open fun addRequestHeaders(moveMethod: MoveMethod) {
//Adding this because the library has an error with override
if (moveMethod.forceOverride) {
moveMethod.setRequestHeader(OVERWRITE, TRUE)
}
}
private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
@ -129,5 +143,7 @@ open class MoveRemoteFileOperation(
companion object {
private const val MOVE_READ_TIMEOUT = 10L
private const val MOVE_CONNECTION_TIMEOUT = 6L
private const val OVERWRITE = "overwrite"
private const val TRUE = "T"
}
}

View File

@ -46,7 +46,10 @@ import java.util.concurrent.TimeUnit
* @author David González Verdugo
*/
class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation<RemoteFile>() {
class ReadRemoteFileOperation(
val remotePath: String,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<RemoteFile>() {
/**
* Performs the read operation.
@ -57,9 +60,9 @@ class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation<RemoteFi
override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteFile> {
try {
val propFind = PropfindMethod(
url = URL("${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)}"),
url = getFinalWebDavUrl(),
depth = DEPTH_0,
propertiesToRequest = DavUtils.allPropset
propertiesToRequest = DavUtils.allPropSet
).apply {
setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS)
setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
@ -69,10 +72,11 @@ class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation<RemoteFi
Timber.i("Read remote file $remotePath with status ${propFind.statusCode}")
return if (isSuccess(status)) {
// TODO: Remove that !!
val remoteFile = RemoteFile.getRemoteFileFromDav(
propFind.root!!,
AccountUtils.getUserId(mAccount, mContext), mAccount.name
davResource = propFind.root!!,
userId = AccountUtils.getUserId(mAccount, mContext),
userName = mAccount.name,
spaceWebDavUrl = spaceWebDavUrl,
)
RemoteOperationResult<RemoteFile>(RemoteOperationResult.ResultCode.OK).apply {
@ -88,6 +92,12 @@ class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation<RemoteFi
}
}
private fun getFinalWebDavUrl(): URL {
val baseWebDavUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
return URL(baseWebDavUrl + WebdavUtils.encodePath(remotePath))
}
private fun isSuccess(status: Int) = status.isOneOf(HTTP_MULTI_STATUS, HTTP_OK)
companion object {

View File

@ -48,7 +48,8 @@ import java.net.URL
* @author David González Verdugo
*/
class ReadRemoteFolderOperation(
val remotePath: String
val remotePath: String,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<ArrayList<RemoteFile>>() {
/**
@ -61,9 +62,9 @@ class ReadRemoteFolderOperation(
PropertyRegistry.register(OCShareTypes.Factory())
val propfindMethod = PropfindMethod(
URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)),
getFinalWebDavUrl(),
DavConstants.DEPTH_1,
DavUtils.allPropset
DavUtils.allPropSet
)
val status = client.executeHttpMethod(propfindMethod)
@ -71,12 +72,11 @@ class ReadRemoteFolderOperation(
if (isSuccess(status)) {
val mFolderAndFiles = ArrayList<RemoteFile>()
// parse data from remote folder
// TODO: Remove that !!
val remoteFolder = RemoteFile.getRemoteFileFromDav(
davResource = propfindMethod.root!!,
userId = AccountUtils.getUserId(mAccount, mContext),
userName = mAccount.name
userName = mAccount.name,
spaceWebDavUrl = spaceWebDavUrl,
)
mFolderAndFiles.add(remoteFolder)
@ -85,7 +85,8 @@ class ReadRemoteFolderOperation(
val remoteFile = RemoteFile.getRemoteFileFromDav(
davResource = resource,
userId = AccountUtils.getUserId(mAccount, mContext),
userName = mAccount.name
userName = mAccount.name,
spaceWebDavUrl = spaceWebDavUrl,
)
mFolderAndFiles.add(remoteFile)
}
@ -107,5 +108,11 @@ class ReadRemoteFolderOperation(
}
}
private fun getFinalWebDavUrl(): URL {
val baseWebDavUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
return URL(baseWebDavUrl + WebdavUtils.encodePath(remotePath))
}
private fun isSuccess(status: Int): Boolean = status.isOneOf(HTTP_OK, HTTP_MULTI_STATUS)
}

View File

@ -26,6 +26,7 @@ package com.owncloud.android.lib.resources.files
import android.net.Uri
import android.os.Parcelable
import androidx.annotation.VisibleForTesting
import at.bitfire.dav4jvm.PropStat
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.Response
@ -38,8 +39,6 @@ import at.bitfire.dav4jvm.property.OCId
import at.bitfire.dav4jvm.property.OCPermissions
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.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
@ -50,7 +49,6 @@ import kotlinx.parcelize.Parcelize
import okhttp3.HttpUrl
import timber.log.Timber
import java.io.File
import java.math.BigDecimal
/**
* Contains the data of a Remote File from a WebDavEntry
@ -72,8 +70,6 @@ data class RemoteFile(
var permissions: String? = null,
var remoteId: String? = null,
var size: Long = 0,
var quotaUsedBytes: BigDecimal? = null,
var quotaAvailableBytes: BigDecimal? = null,
var privateLink: String? = null,
var owner: String,
var sharedByLink: Boolean = false,
@ -100,8 +96,13 @@ data class RemoteFile(
const val MIME_DIR = "DIR"
const val MIME_DIR_UNIX = "httpd/unix-directory"
fun getRemoteFileFromDav(davResource: Response, userId: String, userName: String): RemoteFile {
val remotePath = getRemotePathFromUrl(davResource.href, userId)
fun getRemoteFileFromDav(
davResource: Response,
userId: String,
userName: String,
spaceWebDavUrl: String? = null
): RemoteFile {
val remotePath = getRemotePathFromUrl(davResource.href, userId, spaceWebDavUrl)
val remoteFile = RemoteFile(remotePath = remotePath, owner = userName)
val properties = getPropertiesEvenIfPostProcessing(davResource)
@ -131,12 +132,6 @@ data class RemoteFile(
is OCSize -> {
remoteFile.size = property.size
}
is QuotaUsedBytes -> {
remoteFile.quotaUsedBytes = BigDecimal.valueOf(property.quotaUsedBytes)
}
is QuotaAvailableBytes -> {
remoteFile.quotaAvailableBytes = BigDecimal.valueOf(property.quotaAvailableBytes)
}
is OCPrivatelink -> {
remoteFile.privateLink = property.link
}
@ -164,15 +159,25 @@ data class RemoteFile(
* Retrieves a relative path from a remote file url
*
*
* Example: url:port/remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt
* Example legacy:
* /remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt
*
* Example spaces:
* /dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$05bca744-d89f-4e9c-a990-25a0d7f03fe9/Documents/text.txt => /Documents/text.txt
*
* @param url remote file url
* @param userId file owner
* @param spaceWebDavUrl custom web dav url for space
* @return remote relative path of the file
*/
private fun getRemotePathFromUrl(url: HttpUrl, userId: String): String {
val davFilesPath = OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId
val absoluteDavPath = Uri.decode(url.encodedPath)
@VisibleForTesting
fun getRemotePathFromUrl(
url: HttpUrl,
userId: String,
spaceWebDavUrl: String? = null,
): String {
val davFilesPath = spaceWebDavUrl ?: (OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId)
val absoluteDavPath = if (spaceWebDavUrl != null) Uri.decode(url.toString()) else Uri.decode(url.encodedPath)
val pathToOc = absoluteDavPath.split(davFilesPath).first()
return absoluteDavPath.replace(pathToOc + davFilesPath, "")
}

View File

@ -24,7 +24,6 @@
package com.owncloud.android.lib.resources.files
import android.net.Uri
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_NO_CONTENT
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
@ -46,7 +45,8 @@ import java.net.URL
* @author Abel García de Prada
*/
open class RemoveRemoteFileOperation(
private val remotePath: String
private val remotePath: String,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<Unit>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
@ -54,7 +54,7 @@ open class RemoveRemoteFileOperation(
try {
val srcWebDavUri = getSrcWebDavUriForClient(client)
val deleteMethod = DeleteMethod(
URL(srcWebDavUri.toString() + WebdavUtils.encodePath(remotePath))
URL(srcWebDavUri + WebdavUtils.encodePath(remotePath))
)
val status = client.executeHttpMethod(deleteMethod)
@ -75,7 +75,7 @@ open class RemoveRemoteFileOperation(
* For standard removals, we will use [OwnCloudClient.getUserFilesWebDavUri].
* In case we need a different source Uri, override this method.
*/
open fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.userFilesWebDavUri
open fun getSrcWebDavUriForClient(client: OwnCloudClient): String = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
private fun isSuccess(status: Int) = status.isOneOf(HTTP_OK, HTTP_NO_CONTENT)
}

View File

@ -48,6 +48,7 @@ class RenameRemoteFileOperation(
private val oldRemotePath: String,
private val newName: String,
isFolder: Boolean,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<Unit>() {
private var newRemotePath: String
@ -75,8 +76,8 @@ class RenameRemoteFileOperation(
}
val moveMethod: MoveMethod = MoveMethod(
url = URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(oldRemotePath)),
destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(newRemotePath),
url = URL((spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(oldRemotePath)),
destinationUrl = (spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(newRemotePath),
).apply {
setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.MILLISECONDS)
setConnectionTimeout(RENAME_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
@ -105,7 +106,7 @@ class RenameRemoteFileOperation(
* @return 'True' if the target path is already used by an existing file.
*/
private fun targetPathIsUsed(client: OwnCloudClient): Boolean {
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation(newRemotePath, false)
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation(newRemotePath, true)
val exists = checkPathExistenceRemoteOperation.execute(client)
return exists.isSuccess
}

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
* Copyright (C) 2023 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
@ -48,6 +48,7 @@ import java.util.concurrent.atomic.AtomicBoolean
* @author masensio
* @author David González Verdugo
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
*/
open class UploadFileFromFileSystemOperation(
val localPath: String,
@ -55,6 +56,7 @@ open class UploadFileFromFileSystemOperation(
val mimeType: String,
val lastModifiedTimestamp: String,
val requiredEtag: String?,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<Unit>() {
protected val cancellationRequested = AtomicBoolean(false)
@ -97,7 +99,8 @@ open class UploadFileFromFileSystemOperation(
synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) }
}
putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)), fileRequestBody!!).apply {
val baseStringUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
putMethod = PutMethod(URL(baseStringUrl + WebdavUtils.encodePath(remotePath)), fileRequestBody!!).apply {
retryOnConnectionFailure = false
if (!requiredEtag.isNullOrBlank()) {
addRequestHeader(HttpConstants.IF_MATCH_HEADER, requiredEtag)

View File

@ -24,10 +24,9 @@
*/
package com.owncloud.android.lib.resources.files.chunks
import android.net.Uri
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation
class RemoveRemoteChunksFolderOperation(remotePath: String) : RemoveRemoteFileOperation(remotePath) {
override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri
override fun getSrcWebDavUriForClient(client: OwnCloudClient): String = client.uploadsWebDavUri.toString()
}

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
* Copyright (C) 2023 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
@ -28,22 +28,25 @@ import com.owncloud.android.lib.resources.Service
import com.owncloud.android.lib.resources.files.RemoteFile
interface FileService : Service {
fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult<String>
fun checkPathExistence(
path: String,
isUserLogged: Boolean
isUserLogged: Boolean,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<Boolean>
fun copyFile(
sourceRemotePath: String,
targetRemotePath: String,
): RemoteOperationResult<String>
sourceSpaceWebDavUrl: String?,
targetSpaceWebDavUrl: String?,
replace: Boolean,
): RemoteOperationResult<String?>
fun createFolder(
remotePath: String,
createFullPath: Boolean,
isChunkFolder: Boolean = false
isChunkFolder: Boolean = false,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<Unit>
fun downloadFile(
@ -54,18 +57,23 @@ interface FileService : Service {
fun moveFile(
sourceRemotePath: String,
targetRemotePath: String,
spaceWebDavUrl: String?,
replace: Boolean,
): RemoteOperationResult<Unit>
fun readFile(
remotePath: String
remotePath: String,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<RemoteFile>
fun refreshFolder(
remotePath: String
remotePath: String,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<ArrayList<RemoteFile>>
fun removeFile(
remotePath: String
remotePath: String,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<Unit>
fun renameFile(
@ -73,5 +81,6 @@ interface FileService : Service {
oldRemotePath: String,
newName: String,
isFolder: Boolean,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<Unit>
}

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
* Copyright (C) 2023 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
@ -29,7 +29,6 @@ import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperatio
import com.owncloud.android.lib.resources.files.CopyRemoteFileOperation
import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation
import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation
import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation
import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation
import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation
@ -39,37 +38,43 @@ import com.owncloud.android.lib.resources.files.RenameRemoteFileOperation
import com.owncloud.android.lib.resources.files.services.FileService
class OCFileService(override val client: OwnCloudClient) : FileService {
override fun checkPathExistence(
path: String,
isUserLogged: Boolean
isUserLogged: Boolean,
spaceWebDavUrl: String?,
): RemoteOperationResult<Boolean> =
CheckPathExistenceRemoteOperation(
remotePath = path,
isUserLoggedIn = isUserLogged
isUserLoggedIn = isUserLogged,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult<String> =
GetUrlToOpenInWebRemoteOperation(openWithWebEndpoint = openWebEndpoint, fileId = fileId).execute(client)
override fun copyFile(
sourceRemotePath: String,
targetRemotePath: String
): RemoteOperationResult<String> =
targetRemotePath: String,
sourceSpaceWebDavUrl: String?,
targetSpaceWebDavUrl: String?,
replace: Boolean,
): RemoteOperationResult<String?> =
CopyRemoteFileOperation(
srcRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath
sourceRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath,
sourceSpaceWebDavUrl = sourceSpaceWebDavUrl,
targetSpaceWebDavUrl = targetSpaceWebDavUrl,
forceOverride = replace,
).execute(client)
override fun createFolder(
remotePath: String,
createFullPath: Boolean,
isChunkFolder: Boolean
isChunkFolder: Boolean,
spaceWebDavUrl: String?,
): RemoteOperationResult<Unit> =
CreateRemoteFolderOperation(
remotePath = remotePath,
createFullPath = createFullPath,
isChunksFolder = isChunkFolder
isChunksFolder = isChunkFolder,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
override fun downloadFile(
@ -84,43 +89,55 @@ class OCFileService(override val client: OwnCloudClient) : FileService {
override fun moveFile(
sourceRemotePath: String,
targetRemotePath: String,
spaceWebDavUrl: String?,
replace: Boolean,
): RemoteOperationResult<Unit> =
MoveRemoteFileOperation(
sourceRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath,
spaceWebDavUrl = spaceWebDavUrl,
forceOverride = replace,
).execute(client)
override fun readFile(
remotePath: String
remotePath: String,
spaceWebDavUrl: String?,
): RemoteOperationResult<RemoteFile> =
ReadRemoteFileOperation(
remotePath = remotePath
remotePath = remotePath,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
override fun refreshFolder(
remotePath: String
remotePath: String,
spaceWebDavUrl: String?,
): RemoteOperationResult<ArrayList<RemoteFile>> =
ReadRemoteFolderOperation(
remotePath = remotePath
remotePath = remotePath,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
override fun removeFile(
remotePath: String
remotePath: String,
spaceWebDavUrl: String?,
): RemoteOperationResult<Unit> =
RemoveRemoteFileOperation(
remotePath = remotePath
remotePath = remotePath,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
override fun renameFile(
oldName: String,
oldRemotePath: String,
newName: String,
isFolder: Boolean
isFolder: Boolean,
spaceWebDavUrl: String?,
): RemoteOperationResult<Unit> =
RenameRemoteFileOperation(
oldName = oldName,
oldRemotePath = oldRemotePath,
newName = newName,
isFolder = isFolder
isFolder = isFolder,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
}

View File

@ -89,8 +89,6 @@ class CreateRemoteShareOperation(
var expirationDateInMillis: Long = INIT_EXPIRATION_DATE_IN_MILLIS // Expiration date to set for the public link
var publicUpload: Boolean = false // Upload permissions for the public link (only folders)
var retrieveShareDetails = false // To retrieve more info about the just created share
private fun buildRequestUri(baseUri: Uri) =
@ -99,7 +97,7 @@ class CreateRemoteShareOperation(
.appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT)
.build()
private fun parseResponse(response: String): ShareResponse? {
private fun parseResponse(response: String): ShareResponse {
val moshi = Moshi.Builder().build()
val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareItem::class.java)
val adapter: JsonAdapter<CommonOcsResponse<ShareItem>> = moshi.adapter(commonOcsType)
@ -155,12 +153,10 @@ class CreateRemoteShareOperation(
formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate)
}
if (publicUpload) {
formBodyBuilder.add(PARAM_PUBLIC_UPLOAD, publicUpload.toString())
}
if (password.isNotEmpty()) {
formBodyBuilder.add(PARAM_PASSWORD, password)
}
if (RemoteShare.DEFAULT_PERMISSION != permissions) {
formBodyBuilder.add(PARAM_PERMISSIONS, permissions.toString())
}
@ -207,7 +203,6 @@ class CreateRemoteShareOperation(
private const val PARAM_SHARE_TYPE = "shareType"
private const val PARAM_SHARE_WITH = "shareWith"
private const val PARAM_PASSWORD = "password"
private const val PARAM_PUBLIC_UPLOAD = "publicUpload"
private const val PARAM_PERMISSIONS = "permissions"
//Arguments - constant values

View File

@ -36,13 +36,7 @@ import com.owncloud.android.lib.common.http.HttpConstants.VALUE_FORMAT
import com.owncloud.android.lib.common.http.methods.nonwebdav.DeleteMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.CommonOcsResponse
import com.owncloud.android.lib.resources.shares.responses.ShareItem
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import timber.log.Timber
import java.lang.reflect.Type
import java.net.URL
/**
@ -52,14 +46,10 @@ import java.net.URL
* @author David A. Velasco
* @author David González Verdugo
* @author Fernando Sanz Velasco
*/
/**
* Constructor
*
* @param remoteShareId Share ID
*/
class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation<ShareResponse>() {
class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation<Unit>() {
private fun buildRequestUri(baseUri: Uri) =
baseUri.buildUpon()
@ -68,24 +58,12 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper
.appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT)
.build()
private fun parseResponse(response: String): ShareResponse? {
val moshi = Moshi.Builder().build()
val listOfShareItemType: Type = Types.newParameterizedType(List::class.java, ShareItem::class.java)
val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, listOfShareItemType)
val adapter: JsonAdapter<CommonOcsResponse<List<ShareItem>>> = moshi.adapter(commonOcsType)
return adapter.fromJson(response)?.ocs?.data?.let { listOfShareItems ->
ShareResponse(listOfShareItems.map { shareItem ->
shareItem.toRemoteShare()
})
}
}
private fun onResultUnsuccessful(
method: DeleteMethod,
response: String?,
status: Int
): RemoteOperationResult<ShareResponse> {
Timber.e("Failed response while unshare link ")
): RemoteOperationResult<Unit> {
Timber.e("Failed response while removing share ")
if (response != null) {
Timber.e("*** status code: $status; response message: $response")
} else {
@ -94,17 +72,14 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper
return RemoteOperationResult(method)
}
private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareResponse> {
val result = RemoteOperationResult<ShareResponse>(RemoteOperationResult.ResultCode.OK)
private fun onRequestSuccessful(response: String?): RemoteOperationResult<Unit> {
val result = RemoteOperationResult<Unit>(RemoteOperationResult.ResultCode.OK)
Timber.d("Successful response: $response")
result.data = parseResponse(response!!)
Timber.d("*** Unshare link completed ")
return result
}
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> {
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
val requestUri = buildRequestUri(client.baseUri)
val deleteMethod = DeleteMethod(URL(requestUri.toString())).apply {
@ -129,7 +104,7 @@ class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOper
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
companion object {
//OCS Route
// OCS Route
private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/shares"
}
}

View File

@ -103,13 +103,6 @@ class UpdateRemoteShareOperation
*/
var permissions: Int = DEFAULT_PERMISSION
/**
* Enable upload permissions to update in Share resource.
*
* Null results in no update applied to the upload permission.
*/
var publicUpload: Boolean? = null
var retrieveShareDetails = false // To retrieve more info about the just updated share
private fun buildRequestUri(baseUri: Uri) =
@ -119,7 +112,7 @@ class UpdateRemoteShareOperation
.appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT)
.build()
private fun parseResponse(response: String): ShareResponse? {
private fun parseResponse(response: String): ShareResponse {
val moshi = Moshi.Builder().build()
val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareItem::class.java)
val adapter: JsonAdapter<CommonOcsResponse<ShareItem>> = moshi.adapter(commonOcsType)
@ -181,10 +174,6 @@ class UpdateRemoteShareOperation
formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate)
} // else, ignore - no update
if (publicUpload != null) {
formBodyBuilder.add(PARAM_PUBLIC_UPLOAD, publicUpload.toString())
}
// IMPORTANT: permissions parameter needs to be updated after mPublicUpload parameter,
// otherwise they would be set always as 1 (READ) in the server when mPublicUpload was updated
if (permissions > DEFAULT_PERMISSION) {
@ -233,7 +222,6 @@ class UpdateRemoteShareOperation
private const val PARAM_PASSWORD = "password"
private const val PARAM_EXPIRATION_DATE = "expireDate"
private const val PARAM_PERMISSIONS = "permissions"
private const val PARAM_PUBLIC_UPLOAD = "publicUpload"
//Arguments - constant values
private const val FORMAT_EXPIRATION_DATE = "yyyy-MM-dd"

View File

@ -46,7 +46,6 @@ interface ShareService : Service {
name: String,
password: String,
expirationDate: Long,
publicUpload: Boolean
): RemoteOperationResult<ShareResponse>
fun updateShare(
@ -55,8 +54,7 @@ interface ShareService : Service {
password: String?,
expirationDate: Long,
permissions: Int,
publicUpload: Boolean
): RemoteOperationResult<ShareResponse>
fun deleteShare(remoteId: String): RemoteOperationResult<ShareResponse>
fun deleteShare(remoteId: String): RemoteOperationResult<Unit>
}

View File

@ -36,8 +36,7 @@ import com.owncloud.android.lib.resources.shares.ShareType
import com.owncloud.android.lib.resources.shares.UpdateRemoteShareOperation
import com.owncloud.android.lib.resources.shares.services.ShareService
class OCShareService(override val client: OwnCloudClient) :
ShareService {
class OCShareService(override val client: OwnCloudClient) : ShareService {
override fun getShares(
remoteFilePath: String,
reshares: Boolean,
@ -56,7 +55,6 @@ class OCShareService(override val client: OwnCloudClient) :
name: String,
password: String,
expirationDate: Long,
publicUpload: Boolean
): RemoteOperationResult<ShareResponse> =
CreateRemoteShareOperation(
remoteFilePath,
@ -67,7 +65,6 @@ class OCShareService(override val client: OwnCloudClient) :
this.name = name
this.password = password
this.expirationDateInMillis = expirationDate
this.publicUpload = publicUpload
this.retrieveShareDetails = true
}.execute(client)
@ -77,7 +74,6 @@ class OCShareService(override val client: OwnCloudClient) :
password: String?,
expirationDate: Long,
permissions: Int,
publicUpload: Boolean
): RemoteOperationResult<ShareResponse> =
UpdateRemoteShareOperation(
remoteId
@ -86,11 +82,10 @@ class OCShareService(override val client: OwnCloudClient) :
this.password = password
this.expirationDateInMillis = expirationDate
this.permissions = permissions
this.publicUpload = publicUpload
this.retrieveShareDetails = true
}.execute(client)
override fun deleteShare(remoteId: String): RemoteOperationResult<ShareResponse> =
override fun deleteShare(remoteId: String): RemoteOperationResult<Unit> =
RemoveRemoteShareOperation(
remoteId
).execute(client)

View File

@ -0,0 +1,99 @@
/* 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.spaces
import android.net.Uri
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse
import com.owncloud.android.lib.resources.spaces.responses.SpacesResponseWrapper
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import timber.log.Timber
import java.net.URL
class GetRemoteSpacesOperation : RemoteOperation<List<SpaceResponse>>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<List<SpaceResponse>> {
val requestUri = buildRequestUri(client.baseUri)
val getMethod = GetMethod(URL(requestUri.toString()))
return try {
val status = client.executeHttpMethod(getMethod)
val response = getMethod.getResponseBodyAsString()
if (isSuccess(status)) {
onRequestSuccessful(response)
} else {
onResultUnsuccessful(getMethod, response, status)
}
} catch (e: Exception) {
Timber.e(e, "Exception while getting remote shares")
RemoteOperationResult(e)
}
}
private fun buildRequestUri(baseUri: Uri) =
baseUri.buildUpon()
.appendEncodedPath(GRAPH_API_PATH)
.appendEncodedPath(ENDPOINT_SPACES_LIST)
.build()
private fun parseResponse(response: String): List<SpaceResponse> {
val moshi = Moshi.Builder().build()
val adapter: JsonAdapter<SpacesResponseWrapper> = moshi.adapter(SpacesResponseWrapper::class.java)
return adapter.fromJson(response)?.value ?: listOf()
}
private fun onResultUnsuccessful(
method: GetMethod,
response: String?,
status: Int
): RemoteOperationResult<List<SpaceResponse>> {
Timber.e("Failed response while getting spaces for user")
if (response != null) {
Timber.e("*** status code: $status; response message: $response")
} else {
Timber.e("*** status code: $status")
}
return RemoteOperationResult(method)
}
private fun onRequestSuccessful(response: String?): RemoteOperationResult<List<SpaceResponse>> {
val result = RemoteOperationResult<List<SpaceResponse>>(RemoteOperationResult.ResultCode.OK)
Timber.d("Successful response: $response")
result.data = response?.let { parseResponse(it) } ?: listOf()
Timber.d("*** Fetch of spaces completed and parsed to ${result.data}")
return result
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
companion object {
private const val GRAPH_API_PATH = "graph/v1.0"
private const val ENDPOINT_SPACES_LIST = "me/drives"
}
}

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.spaces.responses
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class SpacesResponseWrapper(
val value: List<SpaceResponse>
)
@JsonClass(generateAdapter = true)
data class SpaceResponse(
val description: String?,
val driveAlias: String,
val driveType: String,
val id: String,
val lastModifiedDateTime: String?,
val name: String,
val owner: OwnerResponse?,
val quota: QuotaResponse?,
val root: RootResponse,
val special: List<SpecialResponse>?,
val webUrl: String,
)
@JsonClass(generateAdapter = true)
data class OwnerResponse(
val user: UserResponse
)
@JsonClass(generateAdapter = true)
data class QuotaResponse(
val remaining: Long?,
val state: String?,
val total: Long,
val used: Long?,
)
@JsonClass(generateAdapter = true)
data class RootResponse(
val eTag: String?,
val id: String,
val webDavUrl: String,
val deleted: DeleteResponse?,
)
@JsonClass(generateAdapter = true)
data class SpecialResponse(
val eTag: String,
val file: FileResponse,
val id: String,
val lastModifiedDateTime: String,
val name: String,
val size: Int,
val specialFolder: SpecialFolderResponse,
val webDavUrl: String
)
@JsonClass(generateAdapter = true)
data class UserResponse(
val id: String
)
@JsonClass(generateAdapter = true)
data class FileResponse(
val mimeType: String
)
@JsonClass(generateAdapter = true)
data class DeleteResponse(
val state: String,
)
@JsonClass(generateAdapter = true)
data class SpecialFolderResponse(
val name: String
)

View File

@ -0,0 +1,34 @@
/* 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.spaces.services
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.spaces.GetRemoteSpacesOperation
import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse
class OCSpacesService(override val client: OwnCloudClient) : SpacesService {
override fun getSpaces(): RemoteOperationResult<List<SpaceResponse>> {
return GetRemoteSpacesOperation().execute(client)
}
}

View File

@ -0,0 +1,31 @@
/* 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.spaces.services
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.Service
import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse
interface SpacesService : Service {
fun getSpaces(): RemoteOperationResult<List<SpaceResponse>>
}

View File

@ -45,7 +45,7 @@ import java.net.URL
/**
* Get the Capabilities from the server
* Save in Result.getData in a RemoteCapability object
* Save Result.getData in a RemoteCapability object
*
* @author masensio
* @author David González Verdugo

View File

@ -1,29 +1,33 @@
/* ownCloud Android Library is available under MIT license
* @author masensio
* @author David González Verdugo
* @author Abel García de Prada
* Copyright (C) 2020 ownCloud GmbH.
/**
* ownCloud Android Library is available under MIT license
*
* 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:
* @author masensio
* @author David González Verdugo
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* Copyright (C) 2022 ownCloud GmbH.
*
* 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.
* 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.status
/**
@ -33,7 +37,7 @@ data class RemoteCapability(
var accountName: String = "",
// Server version
var versionMayor: Int = 0,
var versionMajor: Int = 0,
var versionMinor: Int = 0,
var versionMicro: Int = 0,
var versionString: String = "",
@ -68,7 +72,10 @@ data class RemoteCapability(
var filesUndelete: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesVersioning: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
val filesPrivateLinks: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
val filesAppProviders: List<RemoteOCISProvider>?,
val filesAppProviders: List<RemoteAppProviders>?,
// Spaces
val spaces: RemoteSpaces?,
) {
/**
* Enum for Boolean Type in capabilities, with values:
@ -101,7 +108,7 @@ data class RemoteCapability(
}
}
data class RemoteOCISProvider(
data class RemoteAppProviders(
val enabled: Boolean,
val version: String,
val appsUrl: String?,
@ -109,4 +116,10 @@ data class RemoteCapability(
val openWebUrl: String?,
val newUrl: String?,
)
data class RemoteSpaces(
val enabled: Boolean,
val projects: Boolean,
val shareJail: Boolean,
)
}

View File

@ -1,32 +1,35 @@
/* ownCloud Android Library is available under MIT license
* @author Abel García de Prada
* Copyright (C) 2020 ownCloud GmbH.
/**
* ownCloud Android Library is available under MIT license
*
* 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:
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* Copyright (C) 2022 ownCloud GmbH.
*
* 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.
* 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.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.owncloud.android.lib.resources.status.RemoteCapability.*
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@ -37,7 +40,7 @@ data class CapabilityResponse(
val capabilities: Capabilities?
) {
fun toRemoteCapability(): RemoteCapability = RemoteCapability(
versionMayor = serverVersion?.major ?: 0,
versionMajor = serverVersion?.major ?: 0,
versionMinor = serverVersion?.minor ?: 0,
versionMicro = serverVersion?.micro ?: 0,
versionString = serverVersion?.string ?: "",
@ -70,10 +73,11 @@ data class CapabilityResponse(
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() },
filesAppProviders = capabilities?.fileCapabilities?.appProviders?.map { it.toAppProviders() },
filesSharingFederationIncoming = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.incoming),
filesSharingFederationOutgoing = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.outgoing),
filesSharingUserProfilePicture = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingUser?.profilePicture),
spaces = capabilities?.spacesCapabilities?.toSpaces(),
)
}
@ -86,7 +90,9 @@ data class Capabilities(
@Json(name = "files")
val fileCapabilities: FileCapabilities?,
@Json(name = "dav")
val davCapabilities: DavCapabilities?
val davCapabilities: DavCapabilities?,
@Json(name = "spaces")
val spacesCapabilities: SpacesCapabilities?,
)
@JsonClass(generateAdapter = true)
@ -182,7 +188,7 @@ data class AppProvider(
@Json(name = "new_url")
val newUrl: String?,
) {
fun toOCISProvider() = RemoteOCISProvider(enabled, version, appsUrl, openUrl, openWebUrl, newUrl)
fun toAppProviders() = RemoteAppProviders(enabled, version, appsUrl, openUrl, openWebUrl, newUrl)
}
@JsonClass(generateAdapter = true)
@ -190,6 +196,16 @@ data class DavCapabilities(
val chunking: String?
)
@JsonClass(generateAdapter = true)
data class SpacesCapabilities(
val enabled: Boolean,
val projects: Boolean,
@Json(name = "share_jail")
val shareJail: Boolean,
) {
fun toSpaces() = RemoteSpaces(enabled, projects, shareJail)
}
@JsonClass(generateAdapter = true)
data class ServerVersion(
var major: Int?,

View File

@ -1,8 +1,6 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* @author David González Verdugo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
@ -32,8 +30,7 @@ import com.owncloud.android.lib.resources.status.GetRemoteCapabilitiesOperation
import com.owncloud.android.lib.resources.status.RemoteCapability
import com.owncloud.android.lib.resources.status.services.CapabilityService
class OCCapabilityService(override val client: OwnCloudClient) :
CapabilityService {
class OCCapabilityService(override val client: OwnCloudClient) : CapabilityService {
override fun getCapabilities(): RemoteOperationResult<RemoteCapability> =
GetRemoteCapabilitiesOperation().execute(client)
}

View File

@ -1,8 +1,6 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* @author Abel García de Prada
*
* 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
@ -26,7 +24,6 @@
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
@ -39,16 +36,16 @@ class OCServerInfoService : ServerInfoService {
override fun checkPathExistence(
path: String,
isUserLoggedIn: Boolean,
client: OwnCloudClient
client: OwnCloudClient,
): RemoteOperationResult<Boolean> =
CheckPathExistenceRemoteOperation(
remotePath = path,
isUserLoggedIn = true
isUserLoggedIn = isUserLoggedIn,
).execute(client)
override fun getRemoteStatus(
path: String,
client: OwnCloudClient
client: OwnCloudClient,
): RemoteOperationResult<RemoteServerInfo> =
GetRemoteStatusOperation().execute(client)
}

View File

@ -31,16 +31,16 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
import com.owncloud.android.lib.common.http.methods.nonwebdav.HttpMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.webfinger.responses.WebfingerJrdResponse
import com.owncloud.android.lib.resources.webfinger.responses.WebFingerResponse
import com.squareup.moshi.Moshi
import timber.log.Timber
import java.net.URL
class GetOCInstanceViaWebfingerOperation(
class GetInstancesViaWebFingerOperation(
private val lockupServerDomain: String,
private val rel: String,
private val resource: String,
) : RemoteOperation<String>() {
) : RemoteOperation<List<String>>() {
private fun buildRequestUri() =
Uri.parse(lockupServerDomain).buildUpon()
@ -51,9 +51,9 @@ class GetOCInstanceViaWebfingerOperation(
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
private fun parseResponse(response: String): WebfingerJrdResponse {
private fun parseResponse(response: String): WebFingerResponse {
val moshi = Moshi.Builder().build()
val adapter = moshi.adapter(WebfingerJrdResponse::class.java)
val adapter = moshi.adapter(WebFingerResponse::class.java)
return adapter.fromJson(response)!!
}
@ -61,32 +61,31 @@ class GetOCInstanceViaWebfingerOperation(
method: HttpMethod,
response: String?,
status: Int
): RemoteOperationResult<String> {
Timber.e("Failed requesting webfinger info")
): RemoteOperationResult<List<String>> {
Timber.e("Failed requesting WebFinger info")
if (response != null) {
Timber.e("*** status code: $status; response message: $response")
} else {
Timber.e("*** status code: $status")
}
return RemoteOperationResult<String>(method)
return RemoteOperationResult<List<String>>(method)
}
private fun onRequestSuccessful(rawResponse: String): RemoteOperationResult<String> {
private fun onRequestSuccessful(rawResponse: String): RemoteOperationResult<List<String>> {
val response = parseResponse(rawResponse)
for (i in response.links) {
if (i.rel == rel) {
val operationResult = RemoteOperationResult<String>(RemoteOperationResult.ResultCode.OK)
operationResult.data = i.href
return operationResult
}
}
Timber.e("Could not find ownCloud relevant information in webfinger response: $rawResponse")
throw java.lang.Exception("Could not find ownCloud relevant information in webfinger response")
Timber.d("Successful WebFinger request: $response")
val operationResult = RemoteOperationResult<List<String>>(RemoteOperationResult.ResultCode.OK)
operationResult.data = response.links?.map { it.href } ?: listOf()
return operationResult
}
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
override fun run(client: OwnCloudClient): RemoteOperationResult<List<String>> {
val requestUri = buildRequestUri()
val getMethod = GetMethod(URL(requestUri.toString()))
// First iteration won't follow redirections.
getMethod.followRedirects = false
return try {
val status = client.executeHttpMethod(getMethod)
val response = getMethod.getResponseBodyAsString()!!
@ -97,8 +96,8 @@ class GetOCInstanceViaWebfingerOperation(
onResultUnsuccessful(getMethod, response, status)
}
} catch (e: Exception) {
Timber.e(e, "Requesting webfinger info failed")
RemoteOperationResult<String>(e)
Timber.e(e, "Requesting WebFinger info failed")
RemoteOperationResult<List<String>>(e)
}
}

View File

@ -27,13 +27,13 @@ package com.owncloud.android.lib.resources.webfinger.responses
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class WebfingerJrdResponse(
data class WebFingerResponse(
val subject: String,
val links: List<LinkItem>
val links: List<LinkItem>?
)
@JsonClass(generateAdapter = true)
data class LinkItem(
val rel: String,
val href: String,
val rel: String
)

View File

@ -20,10 +20,11 @@ package com.owncloud.android.lib.resources.webfinger.services
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
interface WebfingerService {
fun getInstanceFromWebfinger(
interface WebFingerService {
fun getInstancesFromWebFinger(
lookupServer: String,
username: String,
resource: String,
rel: String,
client: OwnCloudClient,
): RemoteOperationResult<String>
): RemoteOperationResult<List<String>>
}

View File

@ -19,20 +19,16 @@ package com.owncloud.android.lib.resources.webfinger.services.implementation
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.webfinger.GetOCInstanceViaWebfingerOperation
import com.owncloud.android.lib.resources.webfinger.services.WebfingerService
import com.owncloud.android.lib.resources.webfinger.GetInstancesViaWebFingerOperation
import com.owncloud.android.lib.resources.webfinger.services.WebFingerService
class OCWebfingerService : WebfingerService {
class OCWebFingerService : WebFingerService {
override fun getInstanceFromWebfinger(
override fun getInstancesFromWebFinger(
lookupServer: String,
username: String,
resource: String,
rel: String,
client: OwnCloudClient,
): RemoteOperationResult<String> =
GetOCInstanceViaWebfingerOperation(lookupServer, OWNCLOUD_REL, username).execute(client)
companion object {
private const val OWNCLOUD_REL = "http://webfinger.owncloud/rel/server-instance"
}
): RemoteOperationResult<List<String>> =
GetInstancesViaWebFingerOperation(lookupServer, rel, resource).execute(client)
}

View File

@ -0,0 +1,59 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 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
import android.os.Build
import com.owncloud.android.lib.resources.files.RemoteFile
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.O], manifest = Config.NONE)
class RemoteFileTest {
@Test
fun getRemotePathFromUrl_legacyWebDav() {
val httpUrlToTest = "https://server.url/remote.php/dav/files/username/Documents/text.txt".toHttpUrl()
val expectedRemotePath = "/Documents/text.txt"
val actualRemotePath = RemoteFile.Companion.getRemotePathFromUrl(httpUrlToTest, "username")
assertEquals(expectedRemotePath, actualRemotePath)
}
@Test
fun getRemotePathFromUrl_spacesWebDav() {
val spaceWebDavUrl = "https://server.url/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$05bca744-d89f-4e9c-a990-25a0d7f03fe9"
val httpUrlToTest =
"https://server.url/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$05bca744-d89f-4e9c-a990-25a0d7f03fe9/Documents/text.txt".toHttpUrl()
val expectedRemotePath = "/Documents/text.txt"
val actualRemotePath = RemoteFile.Companion.getRemotePathFromUrl(httpUrlToTest, "username", spaceWebDavUrl)
assertEquals(expectedRemotePath, actualRemotePath)
}
}

View File

@ -8,34 +8,34 @@ import org.junit.Before
import org.junit.Test
import java.io.File
class WebfingerResponseTest {
lateinit var adapter: JsonAdapter<WebfingerJrdResponse>
class WebFingerResponseTest {
lateinit var adapter: JsonAdapter<WebFingerResponse>
private fun loadResponses(fileName: String) = adapter.fromJson(File(fileName).readText())
@Before
fun prepare() {
val moshi = Moshi.Builder().build()
adapter = moshi.adapter(WebfingerJrdResponse::class.java)
adapter = moshi.adapter(WebFingerResponse::class.java)
}
@Test
fun `check rel in too much information - ok`() {
val response = loadResponses(TOO_MUCH_INFORMATION_JSON)!!
Assert.assertEquals("https://gast.somedomain.de", response.links[0].href)
Assert.assertEquals("http://webfinger.owncloud/rel/server-instance", response.links[0].rel)
Assert.assertEquals("https://gast.somedomain.de", response.links!![0].href)
Assert.assertEquals("http://webfinger.owncloud/rel/server-instance", response.links!![0].rel)
}
@Test(expected = JsonDataException::class)
fun `check key value pairs - ko - no href key`() {
val response = loadResponses(BROKEN_JSON)!!
Assert.assertEquals("https://gast.somedomain.de", response.links[0].href)
Assert.assertEquals("https://gast.somedomain.de", response.links!![0].href)
}
@Test(expected = JsonDataException::class)
fun `check key value pairs - ko - no rel key`() {
val response = loadResponses(BROKEN_JSON)!!
Assert.assertEquals("https://gast.somedomain.de", response.links[0].href)
Assert.assertEquals("https://gast.somedomain.de", response.links!![0].href)
}
companion object {