mirror of
https://github.com/owncloud/android-library.git
synced 2025-06-07 16:06:08 +00:00
Compare commits
326 Commits
1.0.10-bet
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
71224c30d1 | ||
|
c82d75b1d8 | ||
|
530999848f | ||
|
b4625d017c | ||
|
2c698cc2a4 | ||
|
bf183fe04d | ||
|
1f8de383b6 | ||
|
8eb435a1c2 | ||
|
052566d205 | ||
|
063c3fa9e9 | ||
|
fa143804d2 | ||
|
5b64876e2c | ||
|
d81ca97f14 | ||
|
5607f76a1d | ||
|
b4137502d2 | ||
|
261075a8ad | ||
|
c2874357f0 | ||
|
e6937b4210 | ||
|
89dd13cec4 | ||
|
ab3a594e5c | ||
|
cbbe044212 | ||
|
17aa1ea7bd | ||
|
cbf6365e93 | ||
|
15bccd3f80 | ||
|
8cee0a37a7 | ||
|
393ec6e07e | ||
|
f35daacdf1 | ||
|
2ac5cf0657 | ||
|
173b12eeca | ||
|
602ab41fcc | ||
|
3a27baad3a | ||
|
14baadd7ea | ||
|
fa65c1a84d | ||
|
c429b575a9 | ||
|
f7d4d27ebb | ||
|
3681f1001a | ||
|
ee5130d3e6 | ||
|
1f93cfaf52 | ||
|
e6e30fc352 | ||
|
c53475da37 | ||
|
c2f32b2a82 | ||
|
6846e25fa3 | ||
|
0e9f9c6391 | ||
|
159dcd6d68 | ||
|
b660ee98fd | ||
|
ff90598a2d | ||
|
0017079a69 | ||
|
2458db1828 | ||
|
e6f3fd2e16 | ||
|
a395787e0b | ||
|
e9f291371d | ||
|
b65efc2b69 | ||
|
78665e8cb0 | ||
|
9c844aae7e | ||
|
2c18e7b679 | ||
|
cb73d537e4 | ||
|
8e4f243031 | ||
|
a65a82cae0 | ||
|
45f53656ba | ||
|
8a4fcb6550 | ||
|
31ccf56e97 | ||
|
0d7a49b3ff | ||
|
7bb94cf289 | ||
|
b1a3402656 | ||
|
f0dda9eb8b | ||
|
4f001550f9 | ||
|
e30246c486 | ||
|
602b7e7548 | ||
|
f8da84d0ad | ||
|
763005b051 | ||
|
d6f969bd91 | ||
|
4200ed324c | ||
|
7ee2c5eb15 | ||
|
b5cd4a1df2 | ||
|
8a8a931e66 | ||
|
148a97cd32 | ||
|
00948ffd73 | ||
|
118646290d | ||
|
defa4d1469 | ||
|
3545686a31 | ||
|
b710070426 | ||
|
a0750c613a | ||
|
9df62a51f3 | ||
|
fa7bfdd597 | ||
|
cb076aa6b9 | ||
|
7ceef58382 | ||
|
a877612fca | ||
|
359e591275 | ||
|
f9bc792ded | ||
|
9adadbddcc | ||
|
1ecb8020b1 | ||
|
b7d3cc2687 | ||
|
2b27b9657c | ||
|
959cb7b015 | ||
|
b59a4e6947 | ||
|
17821e5760 | ||
|
2b79175b5a | ||
|
b25fbc4604 | ||
|
79173af930 | ||
|
c36eedd481 | ||
|
ba37bce0e1 | ||
|
dc7022c531 | ||
|
1f499fb67d | ||
|
12040a1261 | ||
|
7e56412748 | ||
|
71e9bbd2da | ||
|
cbbeaab251 | ||
|
340e114a91 | ||
|
c6584d69fd | ||
|
4c39990edb | ||
|
34cc4c87b1 | ||
|
5449eb7480 | ||
|
33d6141613 | ||
|
f7645c1648 | ||
|
6f33dca672 | ||
|
6fb5350dea | ||
|
24037a7a3e | ||
|
5f01b32b12 | ||
|
f849cd76d4 | ||
|
7c825b2dc3 | ||
|
b3cccfa007 | ||
|
5beb30e07a | ||
|
d736c7cdbf | ||
|
d99444e279 | ||
|
86f16c3460 | ||
|
62dbdb1021 | ||
|
f706595a9a | ||
|
03303ac4f2 | ||
|
039245742c | ||
|
3a996ef583 | ||
|
ec71fa6c23 | ||
|
56248af6ec | ||
|
389c0a0fc6 | ||
|
405da9a729 | ||
|
a02844d2c7 | ||
|
53c99a21c2 | ||
|
5e478036ae | ||
|
cd94e39b7f | ||
|
b0798194be | ||
|
2b5f80bdb9 | ||
|
32891e2cf5 | ||
|
d164b34fe8 | ||
|
c2f7426a3e | ||
|
1e9d8cef42 | ||
|
b083debac9 | ||
|
376d52ac5b | ||
|
e769684920 | ||
|
70bf35f683 | ||
|
1afa124b7d | ||
|
f19b2355b4 | ||
|
c966bf14d0 | ||
|
2c225d8e66 | ||
|
f8a829d6b7 | ||
|
0ea053d612 | ||
|
0be462c9aa | ||
|
32ef5d2125 | ||
|
6282fbd419 | ||
|
6d235fe74a | ||
|
9ab7c139e1 | ||
|
24b850d20c | ||
|
5be4eca0f7 | ||
|
2dc6f30a5e | ||
|
f712a384cd | ||
|
1f9bff1df0 | ||
|
1412117dcd | ||
|
a054adc688 | ||
|
809e641b1a | ||
|
fd8caa42cb | ||
|
45a9a190f8 | ||
|
2931a5494a | ||
|
35fb64c1bd | ||
|
544fc6efd4 | ||
|
16c31988e0 | ||
|
7fe41ce51c | ||
|
aaafdb5ea2 | ||
|
6e503c2cf7 | ||
|
4de236b3c7 | ||
|
f6eb631fbf | ||
|
be758eb5ea | ||
|
d16ab2e643 | ||
|
105830e4f0 | ||
|
09375ce053 | ||
|
fc4ae27bbe | ||
|
344c1e1136 | ||
|
79e4287223 | ||
|
0313c1e103 | ||
|
06e361afaa | ||
|
217216e43f | ||
|
060a988978 | ||
|
e0b81bd11a | ||
|
97a613f168 | ||
|
d8ac22d57e | ||
|
7c77c267f9 | ||
|
2a4195c966 | ||
|
d9dce81ce7 | ||
|
4f3e167efa | ||
|
fc8440cc01 | ||
|
5ca99a0e69 | ||
|
ce761aaec2 | ||
|
c59d1540c7 | ||
|
5582097ca1 | ||
|
e27a968ddb | ||
|
cfd69987e9 | ||
|
b2f6d7f3b1 | ||
|
7ccb86c153 | ||
|
e878ad3931 | ||
|
8d09a5c242 | ||
|
ddb15a33f1 | ||
|
c2c351c912 | ||
|
ca206173df | ||
|
7e4b43e7cb | ||
|
0e82f983b5 | ||
|
0d94058db9 | ||
|
2f952a3a09 | ||
|
39b1fce7f6 | ||
|
351efb7bf2 | ||
|
2edfc17653 | ||
|
e0efa7b3ab | ||
|
5ab6514164 | ||
|
194894637e | ||
|
c662383182 | ||
|
f8c7c12f5b | ||
|
61508c85d1 | ||
|
5619c1daf8 | ||
|
85a3918ff1 | ||
|
7e507abf32 | ||
|
a7e9138593 | ||
|
0ae5cf5ca2 | ||
|
063e7366e5 | ||
|
5cec7b43e2 | ||
|
ff0cde35fe | ||
|
a9a6ca6d4e | ||
|
57882e793e | ||
|
7365c0b126 | ||
|
5bebbe2d3d | ||
|
3aa9e72a22 | ||
|
cc53ed6f68 | ||
|
377572e87a | ||
|
9fc5ddbc39 | ||
|
b649ee0ad6 | ||
|
e6e763882c | ||
|
27e5d367af | ||
|
2b299da415 | ||
|
2c0745f20a | ||
|
1a4b98d232 | ||
|
aef967bf25 | ||
|
5ce81e3b17 | ||
|
4fa986e053 | ||
|
5e8f0b8286 | ||
|
050b98f78b | ||
|
487c4f682a | ||
|
f75f64ab13 | ||
|
dd088d7222 | ||
|
05705722ef | ||
|
36cf45c377 | ||
|
eea5240bd0 | ||
|
20efe90e2d | ||
|
83048b61d8 | ||
|
8a3ba16f96 | ||
|
e6b3df103e | ||
|
37bae10618 | ||
|
10e44627e1 | ||
|
1baeebe5ee | ||
|
fa7d83edb3 | ||
|
46dcb4434d | ||
|
3c46c95ac1 | ||
|
f14abb0bcd | ||
|
0895732ed4 | ||
|
91bd322b68 | ||
|
7baf3e43a5 | ||
|
6285c6c70a | ||
|
b40a394ab1 | ||
|
037a2b30df | ||
|
45fb12df0e | ||
|
27b18c33a9 | ||
|
c24ffcfaa4 | ||
|
fbf0fab800 | ||
|
5852921764 | ||
|
7b5c070175 | ||
|
650f348c35 | ||
|
20070775d7 | ||
|
593da77a04 | ||
|
3025ddce23 | ||
|
ac2f837f9e | ||
|
22719c8f40 | ||
|
12b009e378 | ||
|
5c2be25d66 | ||
|
99e636e8f6 | ||
|
022c486603 | ||
|
b491641eff | ||
|
86bfc3383c | ||
|
58ac89cb30 | ||
|
cfe5793d0d | ||
|
c02a4dea91 | ||
|
e9d23d9cd2 | ||
|
93497ca225 | ||
|
abdbdbe4cc | ||
|
351682cc7f | ||
|
634c4a0f93 | ||
|
307bf93a40 | ||
|
d0e50c4fca | ||
|
bf6f93ff1c | ||
|
2c18ae4ebb | ||
|
594ed2ee1b | ||
|
ccaf5a8e0e | ||
|
8c4a2708c2 | ||
|
3f8ddd0ba9 | ||
|
13344ae622 | ||
|
845b61ea4d | ||
|
257f616b0f | ||
|
7dc81fb74c | ||
|
1287035311 | ||
|
5ca9d5e330 | ||
|
7924561a62 | ||
|
aa65410535 | ||
|
88bb79c5ea | ||
|
87aa7137e7 | ||
|
4df880357c | ||
|
d1765eefb3 | ||
|
bf0ff3ce11 | ||
|
286fd65aff | ||
|
a5574e1e45 | ||
|
0daa19cf90 | ||
|
663e067d08 | ||
|
a60c4909f4 | ||
|
b96a3e9be3 |
5
.editorconfig
Normal file
5
.editorconfig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[*]
|
||||||
|
max_line_length = 150
|
||||||
|
|
||||||
|
[*.{kt, kts}]
|
||||||
|
disabled_rules=no-consecutive-blank-lines,no-wildcard-imports,max-line-length,no-blank-line-before-rbrace,final-newline,indent,no-multi-spaces,comment-spacing,parameter-list-wrapping
|
@ -6,5 +6,5 @@ jobs:
|
|||||||
name: "Validation"
|
name: "Validation"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
@ -49,6 +49,6 @@ ownCloud Android Library uses OkHttp version 4.6.0, licensed under Apache Licens
|
|||||||
|
|
||||||
### Compatibility
|
### 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.
|
ownCloud Android library supports ownCloud server from version 4.5.
|
||||||
|
23
build.gradle
23
build.gradle
@ -1,23 +1,34 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
kotlinVersion = '1.4.31'
|
orgJetbrainsKotlin = '1.8.10'
|
||||||
moshiVersion = "1.11.0"
|
comSquareupMoshi = '1.14.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
mavenCentral()
|
||||||
|
maven { url "https://plugins.gradle.org/m2/" }
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.2'
|
classpath "org.jlleitschuh.gradle:ktlint-gradle:11.1.0"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
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 {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
mavenCentral()
|
||||||
maven { url 'https://jitpack.io' }
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||||
|
apply plugin: "com.google.devtools.ksp"
|
||||||
|
}
|
||||||
|
15
check_code_script.sh
Executable file
15
check_code_script.sh
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
check_license_in_file() {
|
||||||
|
if ! head -n 20 $FILE | grep -q "Permission is hereby granted, free of charge, to any person obtaining a copy"
|
||||||
|
then
|
||||||
|
echo "$FILE does not contain a current copyright header"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for FILE in $(find owncloudComLibrary/src -name "*.java" -o -name "*.kt")
|
||||||
|
do
|
||||||
|
check_license_in_file
|
||||||
|
done
|
||||||
|
|
||||||
|
./gradlew ktlintFormat
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
269
gradlew
vendored
269
gradlew
vendored
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -17,67 +17,101 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
@ -106,80 +140,95 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
warn "Could not query maximum file descriptor limit"
|
||||||
fi
|
esac
|
||||||
ulimit -n $MAX_FD
|
case $MAX_FD in #(
|
||||||
if [ $? -ne 0 ] ; then
|
'' | soft) :;; #(
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
*)
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
else
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
0) set -- ;;
|
|
||||||
1) set -- "$args0" ;;
|
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=`save "$@"`
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
@ -1,47 +1,43 @@
|
|||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'com.google.devtools.ksp'
|
||||||
|
apply plugin: 'kotlin-parcelize'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api 'com.squareup.okhttp3:okhttp:4.6.0'
|
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.gitlab.ownclouders:dav4android:oc_support_2.1.5'
|
||||||
api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2'
|
api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2'
|
||||||
|
|
||||||
// Moshi
|
// Moshi
|
||||||
implementation("com.squareup.moshi:moshi-kotlin:$moshiVersion") {
|
implementation("com.squareup.moshi:moshi-kotlin:$comSquareupMoshi") {
|
||||||
exclude module: "kotlin-reflect"
|
exclude module: "kotlin-reflect"
|
||||||
}
|
}
|
||||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
|
implementation 'org.apache.commons:commons-lang3:3.12.0'
|
||||||
|
ksp "com.squareup.moshi:moshi-kotlin-codegen:$comSquareupMoshi"
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
testImplementation 'org.robolectric:robolectric:4.3.1'
|
testImplementation 'org.robolectric:robolectric:4.10'
|
||||||
|
debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.6.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 33
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 21
|
minSdkVersion 23
|
||||||
targetSdkVersion 29
|
targetSdkVersion 33
|
||||||
|
|
||||||
versionCode = 10000901
|
|
||||||
versionName = "1.0.10-beta.1"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lintOptions {
|
lint {
|
||||||
abortOnError false
|
abortOnError false
|
||||||
ignoreWarnings true
|
ignoreWarnings true
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
testOptions {
|
testOptions {
|
||||||
unitTests {
|
unitTests {
|
||||||
includeAndroidResources = true
|
includeAndroidResources = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
namespace 'com.owncloud.android.lib'
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2020 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.lib.common.http
|
||||||
|
|
||||||
|
import com.facebook.stetho.okhttp3.StethoInterceptor
|
||||||
|
|
||||||
|
object DebugInterceptorFactory {
|
||||||
|
fun getInterceptor() = StethoInterceptor()
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- ownCloud Android Library is available under MIT license
|
<!-- 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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -23,16 +23,8 @@
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<manifest package="com.owncloud.android.lib"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
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" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -0,0 +1,221 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2016 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.lib.common
|
||||||
|
|
||||||
|
import android.accounts.AccountManager
|
||||||
|
import android.accounts.AccountsException
|
||||||
|
import android.content.Context
|
||||||
|
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials
|
||||||
|
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
|
||||||
|
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
|
||||||
|
import com.owncloud.android.lib.resources.status.RemoteServerInfo
|
||||||
|
import org.apache.commons.lang3.exception.ExceptionUtils
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConnectionValidator
|
||||||
|
*
|
||||||
|
* @author Christian Schabesberger
|
||||||
|
*/
|
||||||
|
class ConnectionValidator(
|
||||||
|
val context: Context,
|
||||||
|
private val clearCookiesOnValidation: Boolean
|
||||||
|
) {
|
||||||
|
fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager, context: Context): Boolean {
|
||||||
|
try {
|
||||||
|
var validationRetryCount = 0
|
||||||
|
val client = OwnCloudClient(baseClient.baseUri, null, false, singleSessionManager, context)
|
||||||
|
if (clearCookiesOnValidation) {
|
||||||
|
client.clearCookies()
|
||||||
|
} else {
|
||||||
|
client.cookiesForBaseUri = baseClient.cookiesForBaseUri
|
||||||
|
}
|
||||||
|
|
||||||
|
client.account = baseClient.account
|
||||||
|
client.credentials = baseClient.credentials
|
||||||
|
while (validationRetryCount < VALIDATION_RETRY_COUNT) {
|
||||||
|
Timber.d("validationRetryCount %d", validationRetryCount)
|
||||||
|
var successCounter = 0
|
||||||
|
var failCounter = 0
|
||||||
|
|
||||||
|
client.setFollowRedirects(true)
|
||||||
|
if (isOwnCloudStatusOk(client)) {
|
||||||
|
successCounter++
|
||||||
|
} else {
|
||||||
|
failCounter++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the part where we try to check if we can access the parts where we have to be logged in... if we are not logged in
|
||||||
|
if (baseClient.credentials !is OwnCloudAnonymousCredentials) {
|
||||||
|
client.setFollowRedirects(false)
|
||||||
|
val contentReply = canAccessRootFolder(client)
|
||||||
|
if (contentReply.httpCode == HttpConstants.HTTP_OK) {
|
||||||
|
if (contentReply.data == true) { //if data is true it means that the content reply was ok
|
||||||
|
successCounter++
|
||||||
|
} else {
|
||||||
|
failCounter++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
failCounter++
|
||||||
|
if (contentReply.httpCode == HttpConstants.HTTP_UNAUTHORIZED) {
|
||||||
|
checkUnauthorizedAccess(client, singleSessionManager, contentReply.httpCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (successCounter >= failCounter) {
|
||||||
|
baseClient.credentials = client.credentials
|
||||||
|
baseClient.cookiesForBaseUri = client.cookiesForBaseUri
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
validationRetryCount++
|
||||||
|
}
|
||||||
|
Timber.d("Could not authenticate or get valid data from owncloud")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.d(ExceptionUtils.getStackTrace(e))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun 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
|
||||||
|
// return reply.httpCode == HttpConstants.HTTP_OK &&
|
||||||
|
return !reply.isException &&
|
||||||
|
reply.data != null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOwnCloudStatus(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> {
|
||||||
|
val remoteStatusOperation = GetRemoteStatusOperation()
|
||||||
|
return remoteStatusOperation.execute(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canAccessRootFolder(client: OwnCloudClient): RemoteOperationResult<Boolean> {
|
||||||
|
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation("/", true)
|
||||||
|
return checkPathExistenceRemoteOperation.execute(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if credentials should be invalidated according the to the HTTPS status
|
||||||
|
* of a network request just performed.
|
||||||
|
*
|
||||||
|
* @param httpStatusCode Result of the last request ran with the 'credentials' belows.
|
||||||
|
* @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or
|
||||||
|
* cannot be invalidated with the given arguments.
|
||||||
|
*/
|
||||||
|
private fun shouldInvalidateAccountCredentials(credentials: OwnCloudCredentials, account: OwnCloudAccount, httpStatusCode: Int): Boolean {
|
||||||
|
var shouldInvalidateAccountCredentials = httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED
|
||||||
|
shouldInvalidateAccountCredentials = shouldInvalidateAccountCredentials and // real credentials
|
||||||
|
(credentials !is OwnCloudAnonymousCredentials)
|
||||||
|
|
||||||
|
// test if have all the needed to effectively invalidate ...
|
||||||
|
shouldInvalidateAccountCredentials =
|
||||||
|
shouldInvalidateAccountCredentials and (account.savedAccount != null)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates credentials stored for the given account in the system [AccountManager] and in
|
||||||
|
* current [SingleSessionManager.getDefaultSingleton] instance.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* [.shouldInvalidateAccountCredentials] should be called first.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) {
|
||||||
|
Timber.i("Invalidating account credentials for account $account")
|
||||||
|
val am = AccountManager.get(context)
|
||||||
|
am.invalidateAuthToken(
|
||||||
|
account.savedAccount.type,
|
||||||
|
credentials.authToken
|
||||||
|
)
|
||||||
|
am.clearPassword(account.savedAccount) // being strict, only needed for Basic Auth credentials
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the status code of an execution and decides if should be repeated with fresh credentials.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Invalidates current credentials if the request failed as anauthorized.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Refresh current credentials if possible, and marks a retry.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean {
|
||||||
|
var credentialsWereRefreshed = false
|
||||||
|
val account = client.account
|
||||||
|
val credentials = account.credentials
|
||||||
|
if (shouldInvalidateAccountCredentials(credentials, account, status)) {
|
||||||
|
invalidateAccountCredentials(account, credentials)
|
||||||
|
|
||||||
|
if (credentials.authTokenCanBeRefreshed()) {
|
||||||
|
try {
|
||||||
|
// This command does the actual refresh
|
||||||
|
Timber.i("Trying to refresh auth token for account $account")
|
||||||
|
account.loadCredentials(context)
|
||||||
|
// if mAccount.getCredentials().length() == 0 --> refresh failed
|
||||||
|
client.credentials = account.credentials
|
||||||
|
credentialsWereRefreshed = true
|
||||||
|
} catch (e: AccountsException) {
|
||||||
|
Timber.e(
|
||||||
|
e, "Error while trying to refresh auth token for %s\ntrace: %s",
|
||||||
|
account.savedAccount.name,
|
||||||
|
ExceptionUtils.getStackTrace(e)
|
||||||
|
)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Timber.e(
|
||||||
|
e, "Error while trying to refresh auth token for %s\ntrace: %s",
|
||||||
|
account.savedAccount.name,
|
||||||
|
ExceptionUtils.getStackTrace(e)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!credentialsWereRefreshed) {
|
||||||
|
// if credentials are not refreshed, client must be removed
|
||||||
|
// from the OwnCloudClientManager to prevent it is reused once and again
|
||||||
|
Timber.w("Credentials were not refreshed, client will be removed from the Session Manager to prevent using it over and over")
|
||||||
|
singleSessionManager.removeClientFor(account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else: onExecute will finish with status 401
|
||||||
|
}
|
||||||
|
return credentialsWereRefreshed
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val VALIDATION_RETRY_COUNT = 3
|
||||||
|
}
|
||||||
|
}
|
@ -25,11 +25,9 @@
|
|||||||
|
|
||||||
package com.owncloud.android.lib.common;
|
package com.owncloud.android.lib.common;
|
||||||
|
|
||||||
import android.accounts.AccountManager;
|
import android.content.Context;
|
||||||
import android.accounts.AccountsException;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import at.bitfire.dav4jvm.exception.HttpException;
|
|
||||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
|
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
|
||||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
|
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
|
||||||
@ -37,9 +35,7 @@ import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory
|
|||||||
import com.owncloud.android.lib.common.http.HttpClient;
|
import com.owncloud.android.lib.common.http.HttpClient;
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants;
|
import com.owncloud.android.lib.common.http.HttpConstants;
|
||||||
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
|
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
|
||||||
import com.owncloud.android.lib.common.network.RedirectionPath;
|
|
||||||
import com.owncloud.android.lib.common.utils.RandomUtils;
|
import com.owncloud.android.lib.common.utils.RandomUtils;
|
||||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
|
|
||||||
import okhttp3.Cookie;
|
import okhttp3.Cookie;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
@ -47,42 +43,56 @@ import timber.log.Timber;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
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.AUTHORIZATION_HEADER;
|
||||||
import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID;
|
import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY;
|
||||||
|
|
||||||
public class OwnCloudClient extends HttpClient {
|
public class OwnCloudClient extends HttpClient {
|
||||||
|
|
||||||
public static final String WEBDAV_FILES_PATH_4_0 = "/remote.php/dav/files/";
|
public static final String WEBDAV_FILES_PATH_4_0 = "/remote.php/dav/files/";
|
||||||
public static final String WEBDAV_PATH_4_0_AND_LATER = "/remote.php/dav";
|
|
||||||
public static final String STATUS_PATH = "/status.php";
|
public static final String STATUS_PATH = "/status.php";
|
||||||
private static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/";
|
private static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/";
|
||||||
private static final int MAX_REDIRECTIONS_COUNT = 3;
|
private static final int MAX_RETRY_COUNT = 2;
|
||||||
private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1;
|
|
||||||
|
|
||||||
private static byte[] sExhaustBuffer = new byte[1024];
|
|
||||||
private static int sIntanceCounter = 0;
|
private static int sIntanceCounter = 0;
|
||||||
private OwnCloudCredentials mCredentials = null;
|
private OwnCloudCredentials mCredentials = null;
|
||||||
private int mInstanceNumber;
|
private int mInstanceNumber;
|
||||||
private Uri mBaseUri;
|
private Uri mBaseUri;
|
||||||
private OwnCloudVersion mVersion = null;
|
|
||||||
private OwnCloudAccount mAccount;
|
private OwnCloudAccount mAccount;
|
||||||
|
private final ConnectionValidator mConnectionValidator;
|
||||||
|
private Object mRequestMutex = new Object();
|
||||||
|
|
||||||
|
// If set to true a mutex will be used to prevent parallel execution of the execute() method
|
||||||
|
// if false the execute() method can be called even though the mutex is already aquired.
|
||||||
|
// This is used for the ConnectionValidator, which has to be able to execute OperationsWhile all "normal" operations net
|
||||||
|
// to be set on hold.
|
||||||
|
private final Boolean mSynchronizeRequests;
|
||||||
|
|
||||||
private SingleSessionManager mSingleSessionManager = null;
|
private SingleSessionManager mSingleSessionManager = null;
|
||||||
|
|
||||||
private boolean mFollowRedirects;
|
private boolean mFollowRedirects = false;
|
||||||
|
|
||||||
|
public OwnCloudClient(Uri baseUri,
|
||||||
|
ConnectionValidator connectionValidator,
|
||||||
|
boolean synchronizeRequests,
|
||||||
|
SingleSessionManager singleSessionManager,
|
||||||
|
Context context) {
|
||||||
|
super(context);
|
||||||
|
|
||||||
public OwnCloudClient(Uri baseUri) {
|
|
||||||
if (baseUri == null) {
|
if (baseUri == null) {
|
||||||
throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL");
|
throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL");
|
||||||
}
|
}
|
||||||
mBaseUri = baseUri;
|
mBaseUri = baseUri;
|
||||||
|
mSynchronizeRequests = synchronizeRequests;
|
||||||
|
mSingleSessionManager = singleSessionManager;
|
||||||
|
|
||||||
mInstanceNumber = sIntanceCounter++;
|
mInstanceNumber = sIntanceCounter++;
|
||||||
Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient");
|
Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient");
|
||||||
|
|
||||||
clearCredentials();
|
clearCredentials();
|
||||||
clearCookies();
|
clearCookies();
|
||||||
|
mConnectionValidator = connectionValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCredentials() {
|
public void clearCredentials() {
|
||||||
@ -92,117 +102,63 @@ public class OwnCloudClient extends HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int executeHttpMethod(HttpBaseMethod method) throws Exception {
|
public int executeHttpMethod(HttpBaseMethod method) throws Exception {
|
||||||
boolean repeatWithFreshCredentials;
|
if (mSynchronizeRequests) {
|
||||||
|
synchronized (mRequestMutex) {
|
||||||
|
return saveExecuteHttpMethod(method);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return saveExecuteHttpMethod(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int saveExecuteHttpMethod(HttpBaseMethod method) throws Exception {
|
||||||
int repeatCounter = 0;
|
int repeatCounter = 0;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
if (mFollowRedirects) {
|
||||||
|
method.setFollowRedirects(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean retry;
|
||||||
do {
|
do {
|
||||||
|
repeatCounter++;
|
||||||
|
retry = false;
|
||||||
String requestId = RandomUtils.generateRandomUUID();
|
String requestId = RandomUtils.generateRandomUUID();
|
||||||
|
|
||||||
// Header to allow tracing requests in apache and ownCloud logs
|
// Header to allow tracing requests in apache and ownCloud logs
|
||||||
Timber.d("Executing in request with id %s", requestId);
|
Timber.d("Executing in request with id %s", requestId);
|
||||||
method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId);
|
method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId);
|
||||||
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
|
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
|
||||||
|
method.setRequestHeader(HttpConstants.ACCEPT_LANGUAGE_HEADER, Locale.getDefault().getLanguage());
|
||||||
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
|
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
|
||||||
if (mCredentials.getHeaderAuth() != null && method.getRequestHeader(AUTHORIZATION_HEADER) == null) {
|
if (mCredentials.getHeaderAuth() != null && !mCredentials.getHeaderAuth().isEmpty()) {
|
||||||
method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
|
method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
|
||||||
}
|
}
|
||||||
status = method.execute();
|
|
||||||
|
|
||||||
if (mFollowRedirects) {
|
status = method.execute(this);
|
||||||
status = followRedirection(method).getLastStatus();
|
|
||||||
|
if (shouldConnectionValidatorBeCalled(method, status)) {
|
||||||
|
retry = mConnectionValidator.validate(this, mSingleSessionManager, getContext()); // retry on success fail on no success
|
||||||
|
} else if (method.getFollowPermanentRedirects() && status == HTTP_MOVED_PERMANENTLY) {
|
||||||
|
retry = true;
|
||||||
|
method.setFollowRedirects(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter);
|
} while (retry && repeatCounter < MAX_RETRY_COUNT);
|
||||||
if (repeatWithFreshCredentials) {
|
|
||||||
repeatCounter++;
|
|
||||||
}
|
|
||||||
} while (repeatWithFreshCredentials);
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int executeRedirectedHttpMethod(HttpBaseMethod method) throws Exception {
|
private boolean shouldConnectionValidatorBeCalled(HttpBaseMethod method, int status) {
|
||||||
boolean repeatWithFreshCredentials;
|
|
||||||
int repeatCounter = 0;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
do {
|
return mConnectionValidator != null && (
|
||||||
String requestId = RandomUtils.generateRandomUUID();
|
(!(mCredentials instanceof OwnCloudAnonymousCredentials) &&
|
||||||
|
status == HttpConstants.HTTP_UNAUTHORIZED
|
||||||
// Header to allow tracing requests in apache and ownCloud logs
|
) || (!mFollowRedirects &&
|
||||||
Timber.d("Executing in request with id %s", requestId);
|
!method.getFollowRedirects() &&
|
||||||
method.setRequestHeader(OC_X_REQUEST_ID, requestId);
|
status == HttpConstants.HTTP_MOVED_TEMPORARILY
|
||||||
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
|
)
|
||||||
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
|
);
|
||||||
if (mCredentials.getHeaderAuth() != null) {
|
|
||||||
method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
|
|
||||||
}
|
|
||||||
status = method.execute();
|
|
||||||
|
|
||||||
repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter);
|
|
||||||
if (repeatWithFreshCredentials) {
|
|
||||||
repeatCounter++;
|
|
||||||
}
|
|
||||||
} while (repeatWithFreshCredentials);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RedirectionPath followRedirection(HttpBaseMethod method) throws Exception {
|
|
||||||
int redirectionsCount = 0;
|
|
||||||
int status = method.getStatusCode();
|
|
||||||
RedirectionPath redirectionPath = new RedirectionPath(status, MAX_REDIRECTIONS_COUNT);
|
|
||||||
|
|
||||||
while (redirectionsCount < MAX_REDIRECTIONS_COUNT &&
|
|
||||||
(status == HttpConstants.HTTP_MOVED_PERMANENTLY ||
|
|
||||||
status == HttpConstants.HTTP_MOVED_TEMPORARILY ||
|
|
||||||
status == HttpConstants.HTTP_TEMPORARY_REDIRECT)
|
|
||||||
) {
|
|
||||||
|
|
||||||
final String location = method.getResponseHeader(HttpConstants.LOCATION_HEADER) != null
|
|
||||||
? method.getResponseHeader(HttpConstants.LOCATION_HEADER)
|
|
||||||
: method.getResponseHeader(HttpConstants.LOCATION_HEADER_LOWER);
|
|
||||||
|
|
||||||
if (location != null) {
|
|
||||||
Timber.d("#" + mInstanceNumber + "Location to redirect: " + location);
|
|
||||||
|
|
||||||
redirectionPath.addLocation(location);
|
|
||||||
|
|
||||||
// Release the connection to avoid reach the max number of connections per host
|
|
||||||
// due to it will be set a different url
|
|
||||||
exhaustResponse(method.getResponseBodyAsStream());
|
|
||||||
|
|
||||||
method.setUrl(HttpUrl.parse(location));
|
|
||||||
final String destination = method.getRequestHeader("Destination") != null
|
|
||||||
? method.getRequestHeader("Destination")
|
|
||||||
: method.getRequestHeader("destination");
|
|
||||||
|
|
||||||
if (destination != null) {
|
|
||||||
final int suffixIndex = location.lastIndexOf(getUserFilesWebDavUri().toString());
|
|
||||||
final String redirectionBase = location.substring(0, suffixIndex);
|
|
||||||
final String destinationPath = destination.substring(mBaseUri.toString().length());
|
|
||||||
|
|
||||||
method.setRequestHeader("destination", redirectionBase + destinationPath);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
status = executeRedirectedHttpMethod(method);
|
|
||||||
} catch (HttpException e) {
|
|
||||||
if (e.getMessage().contains(Integer.toString(HttpConstants.HTTP_MOVED_TEMPORARILY))) {
|
|
||||||
status = HttpConstants.HTTP_MOVED_TEMPORARILY;
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
redirectionPath.addStatus(status);
|
|
||||||
redirectionsCount++;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Timber.d(" #" + mInstanceNumber + "No location to redirect!");
|
|
||||||
status = HttpConstants.HTTP_NOT_FOUND;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return redirectionPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,7 +185,7 @@ public class OwnCloudClient extends HttpClient {
|
|||||||
return (mCredentials instanceof OwnCloudAnonymousCredentials || mAccount == null)
|
return (mCredentials instanceof OwnCloudAnonymousCredentials || mAccount == null)
|
||||||
? Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0)
|
? Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0)
|
||||||
: Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0 + AccountUtils.getUserId(
|
: Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0 + AccountUtils.getUserId(
|
||||||
mAccount.getSavedAccount(), getContext()
|
mAccount.getSavedAccount(), getContext()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -238,7 +194,7 @@ public class OwnCloudClient extends HttpClient {
|
|||||||
return mCredentials instanceof OwnCloudAnonymousCredentials
|
return mCredentials instanceof OwnCloudAnonymousCredentials
|
||||||
? Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0)
|
? Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0)
|
||||||
: Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0 + AccountUtils.getUserId(
|
: Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0 + AccountUtils.getUserId(
|
||||||
mAccount.getSavedAccount(), getContext()
|
mAccount.getSavedAccount(), getContext()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -273,32 +229,16 @@ public class OwnCloudClient extends HttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCookiesString() {
|
public void setCookiesForBaseUri(List<Cookie> cookies) {
|
||||||
StringBuilder cookiesString = new StringBuilder();
|
|
||||||
List<Cookie> cookieList = getCookiesFromUrl(HttpUrl.parse(mBaseUri.toString()));
|
|
||||||
|
|
||||||
if (cookieList != null) {
|
|
||||||
for (Cookie cookie : cookieList) {
|
|
||||||
cookiesString.append(cookie.toString()).append(";");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cookiesString.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCookiesForCurrentAccount(List<Cookie> cookies) {
|
|
||||||
getOkHttpClient().cookieJar().saveFromResponse(
|
getOkHttpClient().cookieJar().saveFromResponse(
|
||||||
HttpUrl.parse(getAccount().getBaseUri().toString()),
|
HttpUrl.parse(mBaseUri.toString()),
|
||||||
cookies
|
cookies
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OwnCloudVersion getOwnCloudVersion() {
|
public List<Cookie> getCookiesForBaseUri() {
|
||||||
return mVersion;
|
return getOkHttpClient().cookieJar().loadForRequest(
|
||||||
}
|
HttpUrl.parse(mBaseUri.toString()));
|
||||||
|
|
||||||
public void setOwnCloudVersion(OwnCloudVersion version) {
|
|
||||||
mVersion = version;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public OwnCloudAccount getAccount() {
|
public OwnCloudAccount getAccount() {
|
||||||
@ -309,94 +249,6 @@ public class OwnCloudClient extends HttpClient {
|
|||||||
this.mAccount = account;
|
this.mAccount = account;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the status code of an execution and decides if should be repeated with fresh credentials.
|
|
||||||
* <p>
|
|
||||||
* Invalidates current credentials if the request failed as anauthorized.
|
|
||||||
* <p>
|
|
||||||
* Refresh current credentials if possible, and marks a retry.
|
|
||||||
*
|
|
||||||
* @param status
|
|
||||||
* @param repeatCounter
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private boolean checkUnauthorizedAccess(int status, int repeatCounter) {
|
|
||||||
boolean credentialsWereRefreshed = false;
|
|
||||||
|
|
||||||
if (shouldInvalidateAccountCredentials(status)) {
|
|
||||||
boolean invalidated = invalidateAccountCredentials();
|
|
||||||
|
|
||||||
if (invalidated) {
|
|
||||||
if (getCredentials().authTokenCanBeRefreshed() &&
|
|
||||||
repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) {
|
|
||||||
try {
|
|
||||||
mAccount.loadCredentials(getContext());
|
|
||||||
// if mAccount.getCredentials().length() == 0 --> refresh failed
|
|
||||||
setCredentials(mAccount.getCredentials());
|
|
||||||
credentialsWereRefreshed = true;
|
|
||||||
|
|
||||||
} catch (AccountsException | IOException e) {
|
|
||||||
Timber.e(e, "Error while trying to refresh auth token for %s",
|
|
||||||
mAccount.getSavedAccount().name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!credentialsWereRefreshed && mSingleSessionManager != null) {
|
|
||||||
// if credentials are not refreshed, client must be removed
|
|
||||||
// from the OwnCloudClientManager to prevent it is reused once and again
|
|
||||||
mSingleSessionManager.removeClientFor(mAccount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// else: onExecute will finish with status 401
|
|
||||||
}
|
|
||||||
|
|
||||||
return credentialsWereRefreshed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if credentials should be invalidated according the to the HTTPS status
|
|
||||||
* of a network request just performed.
|
|
||||||
*
|
|
||||||
* @param httpStatusCode Result of the last request ran with the 'credentials' belows.
|
|
||||||
* @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or
|
|
||||||
* cannot be invalidated with the given arguments.
|
|
||||||
*/
|
|
||||||
private boolean shouldInvalidateAccountCredentials(int httpStatusCode) {
|
|
||||||
boolean shouldInvalidateAccountCredentials =
|
|
||||||
(httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED);
|
|
||||||
|
|
||||||
shouldInvalidateAccountCredentials &= (mCredentials != null && // real credentials
|
|
||||||
!(mCredentials instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials));
|
|
||||||
|
|
||||||
// test if have all the needed to effectively invalidate ...
|
|
||||||
shouldInvalidateAccountCredentials &= (mAccount != null && mAccount.getSavedAccount() != null && getContext() != null);
|
|
||||||
|
|
||||||
return shouldInvalidateAccountCredentials;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates credentials stored for the given account in the system {@link AccountManager} and in
|
|
||||||
* current {@link SingleSessionManager#getDefaultSingleton()} instance.
|
|
||||||
* <p>
|
|
||||||
* {@link #shouldInvalidateAccountCredentials(int)} should be called first.
|
|
||||||
*
|
|
||||||
* @return 'True' if invalidation was successful, 'false' otherwise.
|
|
||||||
*/
|
|
||||||
private boolean invalidateAccountCredentials() {
|
|
||||||
AccountManager am = AccountManager.get(getContext());
|
|
||||||
am.invalidateAuthToken(
|
|
||||||
mAccount.getSavedAccount().type,
|
|
||||||
mCredentials.getAuthToken()
|
|
||||||
);
|
|
||||||
am.clearPassword(mAccount.getSavedAccount()); // being strict, only needed for Basic Auth credentials
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean followRedirects() {
|
|
||||||
return mFollowRedirects;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFollowRedirects(boolean followRedirects) {
|
public void setFollowRedirects(boolean followRedirects) {
|
||||||
this.mFollowRedirects = followRedirects;
|
this.mFollowRedirects = followRedirects;
|
||||||
}
|
}
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.common;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.http.HttpClient;
|
|
||||||
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation;
|
|
||||||
|
|
||||||
public class OwnCloudClientFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a OwnCloudClient to access a URL and sets the desired parameters for ownCloud
|
|
||||||
* client connections.
|
|
||||||
*
|
|
||||||
* @param uri URL to the ownCloud server; BASE ENTRY POINT, not WebDavPATH
|
|
||||||
* @param context Android context where the OwnCloudClient is being created.
|
|
||||||
* @return A OwnCloudClient object ready to be used
|
|
||||||
*/
|
|
||||||
public static OwnCloudClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) {
|
|
||||||
OwnCloudClient client = new OwnCloudClient(uri);
|
|
||||||
|
|
||||||
client.setFollowRedirects(followRedirects);
|
|
||||||
|
|
||||||
HttpClient.setContext(context);
|
|
||||||
retrieveCookiesFromMiddleware(client);
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void retrieveCookiesFromMiddleware(OwnCloudClient client) {
|
|
||||||
final GetRemoteStatusOperation statusOperation = new GetRemoteStatusOperation();
|
|
||||||
statusOperation.run(client);
|
|
||||||
}
|
|
||||||
}
|
|
@ -31,7 +31,6 @@ import android.net.Uri;
|
|||||||
|
|
||||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
|
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
|
||||||
import com.owncloud.android.lib.common.http.HttpClient;
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -49,6 +48,7 @@ public class SingleSessionManager {
|
|||||||
|
|
||||||
private static SingleSessionManager sDefaultSingleton;
|
private static SingleSessionManager sDefaultSingleton;
|
||||||
private static String sUserAgent;
|
private static String sUserAgent;
|
||||||
|
private static ConnectionValidator sConnectionValidator;
|
||||||
|
|
||||||
private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername = new ConcurrentHashMap<>();
|
private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername = new ConcurrentHashMap<>();
|
||||||
private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername = new ConcurrentHashMap<>();
|
private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername = new ConcurrentHashMap<>();
|
||||||
@ -60,6 +60,14 @@ public class SingleSessionManager {
|
|||||||
return sDefaultSingleton;
|
return sDefaultSingleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setConnectionValidator(ConnectionValidator connectionValidator) {
|
||||||
|
sConnectionValidator = connectionValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConnectionValidator getConnectionValidator() {
|
||||||
|
return sConnectionValidator;
|
||||||
|
}
|
||||||
|
|
||||||
public static String getUserAgent() {
|
public static String getUserAgent() {
|
||||||
return sUserAgent;
|
return sUserAgent;
|
||||||
}
|
}
|
||||||
@ -68,7 +76,23 @@ public class SingleSessionManager {
|
|||||||
sUserAgent = userAgent;
|
sUserAgent = userAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws OperationCanceledException,
|
private static OwnCloudClient createOwnCloudClient(Uri uri,
|
||||||
|
Context context,
|
||||||
|
ConnectionValidator connectionValidator,
|
||||||
|
SingleSessionManager singleSessionManager) {
|
||||||
|
OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true, singleSessionManager, context);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OwnCloudClient getClientFor(OwnCloudAccount account,
|
||||||
|
Context context) throws OperationCanceledException,
|
||||||
|
AuthenticatorException, IOException {
|
||||||
|
return getClientFor(account, context, getConnectionValidator());
|
||||||
|
}
|
||||||
|
|
||||||
|
public OwnCloudClient getClientFor(OwnCloudAccount account,
|
||||||
|
Context context,
|
||||||
|
ConnectionValidator connectionValidator) throws OperationCanceledException,
|
||||||
AuthenticatorException, IOException {
|
AuthenticatorException, IOException {
|
||||||
|
|
||||||
Timber.d("getClientFor starting ");
|
Timber.d("getClientFor starting ");
|
||||||
@ -99,15 +123,34 @@ public class SingleSessionManager {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.v("reusing client for account %s", accountName);
|
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;
|
reusingKnown = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
// no client to reuse - create a new one
|
// no client to reuse - create a new one
|
||||||
client = OwnCloudClientFactory.createOwnCloudClient(
|
client = createOwnCloudClient(
|
||||||
account.getBaseUri(),
|
account.getBaseUri(),
|
||||||
context.getApplicationContext(),
|
context,
|
||||||
true); // TODO remove dependency on OwnCloudClientFactory
|
connectionValidator,
|
||||||
|
this); // TODO remove dependency on OwnCloudClientFactory
|
||||||
|
|
||||||
//the next two lines are a hack because okHttpclient is used as a singleton instead of being an
|
//the next two lines are a hack because okHttpclient is used as a singleton instead of being an
|
||||||
//injected instance that can be deleted when required
|
//injected instance that can be deleted when required
|
||||||
@ -115,7 +158,6 @@ public class SingleSessionManager {
|
|||||||
client.clearCredentials();
|
client.clearCredentials();
|
||||||
|
|
||||||
client.setAccount(account);
|
client.setAccount(account);
|
||||||
HttpClient.setContext(context);
|
|
||||||
|
|
||||||
account.loadCredentials(context);
|
account.loadCredentials(context);
|
||||||
client.setCredentials(account.getCredentials());
|
client.setCredentials(account.getCredentials());
|
||||||
|
@ -52,17 +52,9 @@ public class AccountUtils {
|
|||||||
*/
|
*/
|
||||||
public static String getWebDavUrlForAccount(Context context, Account account)
|
public static String getWebDavUrlForAccount(Context context, Account account)
|
||||||
throws AccountNotFoundException {
|
throws AccountNotFoundException {
|
||||||
String webDavUrlForAccount = "";
|
|
||||||
|
|
||||||
try {
|
return getBaseUrlForAccount(context, account) + OwnCloudClient.WEBDAV_FILES_PATH_4_0
|
||||||
OwnCloudCredentials ownCloudCredentials = getCredentialsForAccount(context, account);
|
+ AccountUtils.getUserId(account, context);
|
||||||
webDavUrlForAccount = getBaseUrlForAccount(context, account) + OwnCloudClient.WEBDAV_FILES_PATH_4_0
|
|
||||||
+ ownCloudCredentials.getUsername();
|
|
||||||
} catch (OperationCanceledException | AuthenticatorException | IOException e) {
|
|
||||||
Timber.e(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return webDavUrlForAccount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,26 +94,6 @@ public class AccountUtils {
|
|||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the stored server version corresponding to an OC account.
|
|
||||||
*
|
|
||||||
* @param account An OC account
|
|
||||||
* @param context Application context
|
|
||||||
* @return Version of the OC server, according to last check
|
|
||||||
*/
|
|
||||||
public static OwnCloudVersion getServerVersionForAccount(Account account, Context context) {
|
|
||||||
AccountManager ama = AccountManager.get(context);
|
|
||||||
OwnCloudVersion version = null;
|
|
||||||
try {
|
|
||||||
String versionString = ama.getUserData(account, Constants.KEY_OC_VERSION);
|
|
||||||
version = new OwnCloudVersion(versionString);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Timber.e(e, "Couldn't get a the server version for an account");
|
|
||||||
}
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return
|
* @return
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
@ -140,6 +112,7 @@ public class AccountUtils {
|
|||||||
String username = AccountUtils.getUsernameForAccount(account);
|
String username = AccountUtils.getUsernameForAccount(account);
|
||||||
|
|
||||||
if (isOauth2) {
|
if (isOauth2) {
|
||||||
|
Timber.i("Trying to retrieve credentials for oAuth account" + account.name);
|
||||||
String accessToken = am.blockingGetAuthToken(
|
String accessToken = am.blockingGetAuthToken(
|
||||||
account,
|
account,
|
||||||
AccountTypeUtils.getAuthTokenTypeAccessToken(account.type),
|
AccountTypeUtils.getAuthTokenTypeAccessToken(account.type),
|
||||||
@ -217,11 +190,6 @@ public class AccountUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Constants {
|
public static class Constants {
|
||||||
/**
|
|
||||||
* Version should be 3 numbers separated by dot so it can be parsed by
|
|
||||||
* {@link OwnCloudVersion}
|
|
||||||
*/
|
|
||||||
public static final String KEY_OC_VERSION = "oc_version";
|
|
||||||
/**
|
/**
|
||||||
* Base url should point to owncloud installation without trailing / ie:
|
* Base url should point to owncloud installation without trailing / ie:
|
||||||
* http://server/path or https://owncloud.server
|
* http://server/path or https://owncloud.server
|
||||||
|
@ -28,7 +28,7 @@ import okhttp3.CookieJar
|
|||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
|
|
||||||
class CookieJarImpl(
|
class CookieJarImpl(
|
||||||
private val sCookieStore: HashMap<String, List<Cookie>>
|
private val cookieStore: HashMap<String, List<Cookie>>
|
||||||
) : CookieJar {
|
) : CookieJar {
|
||||||
|
|
||||||
fun containsCookieWithName(cookies: List<Cookie>, name: String): Boolean {
|
fun containsCookieWithName(cookies: List<Cookie>, name: String): Boolean {
|
||||||
@ -52,12 +52,11 @@ class CookieJarImpl(
|
|||||||
|
|
||||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||||
// Avoid duplicated cookies but update
|
// Avoid duplicated cookies but update
|
||||||
val currentCookies: List<Cookie> = sCookieStore[url.host] ?: ArrayList()
|
val currentCookies: List<Cookie> = cookieStore[url.host] ?: ArrayList()
|
||||||
val updatedCookies: List<Cookie> = getUpdatedCookies(currentCookies, cookies)
|
val updatedCookies: List<Cookie> = getUpdatedCookies(currentCookies, cookies)
|
||||||
sCookieStore[url.host] = updatedCookies
|
cookieStore[url.host] = updatedCookies
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadForRequest(url: HttpUrl) =
|
override fun loadForRequest(url: HttpUrl) =
|
||||||
sCookieStore[url.host] ?: ArrayList()
|
cookieStore[url.host] ?: ArrayList()
|
||||||
|
}
|
||||||
}
|
|
@ -21,26 +21,11 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.sampleclient;
|
|
||||||
|
|
||||||
import android.content.Context;
|
package com.owncloud.android.lib.common.http
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.owncloud.android.lib.resources.files.RemoteFile;
|
import okhttp3.Interceptor
|
||||||
|
|
||||||
public class FilesArrayAdapter extends ArrayAdapter<RemoteFile> {
|
|
||||||
|
|
||||||
public FilesArrayAdapter(Context context, int resource) {
|
|
||||||
super(context, resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
TextView textView = (TextView) super.getView(position, convertView, parent);
|
|
||||||
textView.setText(getItem(position).getRemotePath());
|
|
||||||
return textView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
class DummyInterceptor : Interceptor {
|
||||||
|
override fun intercept(chain: Interceptor.Chain) = chain.proceed(chain.request())
|
||||||
|
}
|
@ -40,7 +40,6 @@ import javax.net.ssl.SSLContext;
|
|||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -54,32 +53,46 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public class HttpClient {
|
public class HttpClient {
|
||||||
private static OkHttpClient sOkHttpClient;
|
private Context mContext;
|
||||||
private static Context sContext;
|
private HashMap<String, List<Cookie>> mCookieStore = new HashMap<>();
|
||||||
private static HashMap<String, List<Cookie>> sCookieStore = new HashMap<>();
|
private LogInterceptor mLogInterceptor = new LogInterceptor();
|
||||||
private static LogInterceptor sLogInterceptor;
|
|
||||||
|
|
||||||
public static OkHttpClient getOkHttpClient() {
|
private OkHttpClient mOkHttpClient = null;
|
||||||
if (sOkHttpClient == null) {
|
|
||||||
try {
|
|
||||||
final X509TrustManager trustManager = new AdvancedX509TrustManager(
|
|
||||||
NetworkUtils.getKnownServersStore(sContext));
|
|
||||||
final SSLSocketFactory sslSocketFactory = getNewSslSocketFactory(trustManager);
|
|
||||||
// Automatic cookie handling, NOT PERSISTENT
|
|
||||||
final CookieJar cookieJar = new CookieJarImpl(sCookieStore);
|
|
||||||
|
|
||||||
// TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok.
|
protected HttpClient(Context context) {
|
||||||
//.hostnameVerifier(new BrowserCompatHostnameVerifier());
|
if (context == null) {
|
||||||
sOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar);
|
Timber.e("Context may not be NULL!");
|
||||||
|
throw new NullPointerException("Context may not be NULL!");
|
||||||
} catch (Exception e) {
|
|
||||||
Timber.e(e, "Could not setup SSL system.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return sOkHttpClient;
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SSLContext getSslContext() throws NoSuchAlgorithmException {
|
public OkHttpClient getOkHttpClient() {
|
||||||
|
if (mOkHttpClient == null) {
|
||||||
|
try {
|
||||||
|
final X509TrustManager trustManager = new AdvancedX509TrustManager(
|
||||||
|
NetworkUtils.getKnownServersStore(mContext));
|
||||||
|
|
||||||
|
final SSLContext sslContext = buildSSLContext();
|
||||||
|
sslContext.init(null, new TrustManager[]{trustManager}, null);
|
||||||
|
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||||
|
|
||||||
|
// Automatic cookie handling, NOT PERSISTENT
|
||||||
|
final CookieJar cookieJar = new CookieJarImpl(mCookieStore);
|
||||||
|
mOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar);
|
||||||
|
|
||||||
|
} catch (NoSuchAlgorithmException nsae) {
|
||||||
|
Timber.e(nsae, "Could not setup SSL system.");
|
||||||
|
throw new RuntimeException("Could not setup okHttp client.", nsae);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Timber.e(e, "Could not setup okHttp client.");
|
||||||
|
throw new RuntimeException("Could not setup okHttp client.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mOkHttpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SSLContext buildSSLContext() throws NoSuchAlgorithmException {
|
||||||
try {
|
try {
|
||||||
return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName());
|
return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName());
|
||||||
} catch (NoSuchAlgorithmException tlsv13Exception) {
|
} catch (NoSuchAlgorithmException tlsv13Exception) {
|
||||||
@ -100,17 +113,11 @@ public class HttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SSLSocketFactory getNewSslSocketFactory(X509TrustManager trustManager)
|
private OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager,
|
||||||
throws NoSuchAlgorithmException, KeyManagementException {
|
CookieJar cookieJar) {
|
||||||
final SSLContext sslContext = getSslContext();
|
|
||||||
sslContext.init(null, new TrustManager[]{trustManager}, null);
|
|
||||||
return sslContext.getSocketFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager,
|
|
||||||
CookieJar cookieJar) {
|
|
||||||
return new OkHttpClient.Builder()
|
return new OkHttpClient.Builder()
|
||||||
.addNetworkInterceptor(getLogInterceptor())
|
.addNetworkInterceptor(getLogInterceptor())
|
||||||
|
.addNetworkInterceptor(DebugInterceptorFactory.INSTANCE.getInterceptor())
|
||||||
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
|
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
|
||||||
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
@ -122,26 +129,19 @@ public class HttpClient {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LogInterceptor getLogInterceptor() {
|
|
||||||
if (sLogInterceptor == null) {
|
|
||||||
sLogInterceptor = new LogInterceptor();
|
|
||||||
}
|
|
||||||
return sLogInterceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
|
|
||||||
return sCookieStore.get(httpUrl.host());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
return sContext;
|
return mContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setContext(Context context) {
|
public LogInterceptor getLogInterceptor() {
|
||||||
sContext = context;
|
return mLogInterceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
|
||||||
|
return mCookieStore.get(httpUrl.host());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCookies() {
|
public void clearCookies() {
|
||||||
sCookieStore.clear();
|
mCookieStore.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ public class HttpConstants {
|
|||||||
public static final String IF_MATCH_HEADER = "If-Match";
|
public static final String IF_MATCH_HEADER = "If-Match";
|
||||||
public static final String IF_NONE_MATCH_HEADER = "If-None-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 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 CONTENT_LENGTH_HEADER = "Content-Length";
|
||||||
public static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length";
|
public static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length";
|
||||||
public static final String OC_X_OC_MTIME_HEADER = "X-OC-Mtime";
|
public static final String OC_X_OC_MTIME_HEADER = "X-OC-Mtime";
|
||||||
@ -56,6 +57,7 @@ public class HttpConstants {
|
|||||||
public static final String OAUTH_HEADER_GRANT_TYPE = "grant_type";
|
public static final String OAUTH_HEADER_GRANT_TYPE = "grant_type";
|
||||||
public static final String OAUTH_HEADER_REDIRECT_URI = "redirect_uri";
|
public static final String OAUTH_HEADER_REDIRECT_URI = "redirect_uri";
|
||||||
public static final String OAUTH_HEADER_REFRESH_TOKEN = "refresh_token";
|
public static final String OAUTH_HEADER_REFRESH_TOKEN = "refresh_token";
|
||||||
|
public static final String OAUTH_HEADER_CODE_VERIFIER = "code_verifier";
|
||||||
|
|
||||||
/***********************************************************************************************************
|
/***********************************************************************************************************
|
||||||
************************************************ CONTENT TYPES ********************************************
|
************************************************ CONTENT TYPES ********************************************
|
||||||
@ -65,6 +67,18 @@ public class HttpConstants {
|
|||||||
public static final String CONTENT_TYPE_JSON = "application/json";
|
public static final String CONTENT_TYPE_JSON = "application/json";
|
||||||
public static final String CONTENT_TYPE_WWW_FORM = "application/x-www-form-urlencoded";
|
public static final String CONTENT_TYPE_WWW_FORM = "application/x-www-form-urlencoded";
|
||||||
|
|
||||||
|
/***********************************************************************************************************
|
||||||
|
************************************************ ARGUMENTS NAMES ********************************************
|
||||||
|
***********************************************************************************************************/
|
||||||
|
|
||||||
|
public static final String PARAM_FORMAT = "format";
|
||||||
|
|
||||||
|
/***********************************************************************************************************
|
||||||
|
************************************************ ARGUMENTS VALUES ********************************************
|
||||||
|
***********************************************************************************************************/
|
||||||
|
|
||||||
|
public static final String VALUE_FORMAT = "json";
|
||||||
|
|
||||||
/***********************************************************************************************************
|
/***********************************************************************************************************
|
||||||
************************************************ STATUS CODES *********************************************
|
************************************************ STATUS CODES *********************************************
|
||||||
***********************************************************************************************************/
|
***********************************************************************************************************/
|
||||||
@ -171,6 +185,7 @@ public class HttpConstants {
|
|||||||
public static final int HTTP_LOCKED = 423;
|
public static final int HTTP_LOCKED = 423;
|
||||||
// 424 Failed Dependency (WebDAV - RFC 2518)
|
// 424 Failed Dependency (WebDAV - RFC 2518)
|
||||||
public static final int HTTP_FAILED_DEPENDENCY = 424;
|
public static final int HTTP_FAILED_DEPENDENCY = 424;
|
||||||
|
public static final int HTTP_TOO_EARLY = 425;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 5xx Client Error
|
* 5xx Client Error
|
||||||
@ -204,4 +219,4 @@ public class HttpConstants {
|
|||||||
* Default timeout for establishing a connection
|
* Default timeout for establishing a connection
|
||||||
*/
|
*/
|
||||||
public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
|
public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
|
||||||
}
|
}
|
||||||
|
@ -41,13 +41,13 @@ object LogBuilder {
|
|||||||
enum class NetworkPetition {
|
enum class NetworkPetition {
|
||||||
REQUEST, RESPONSE;
|
REQUEST, RESPONSE;
|
||||||
|
|
||||||
override fun toString(): String = super.toString().toLowerCase(Locale.ROOT)
|
override fun toString(): String = super.toString().lowercase(Locale.ROOT)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class NetworkNode {
|
enum class NetworkNode {
|
||||||
INFO, HEADER, BODY;
|
INFO, HEADER, BODY;
|
||||||
|
|
||||||
override fun toString(): String = super.toString().toLowerCase(Locale.ROOT)
|
override fun toString(): String = super.toString().lowercase(Locale.ROOT)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,7 +51,7 @@ class LogInterceptor : Interceptor {
|
|||||||
|
|
||||||
val request = chain.request().also {
|
val request = chain.request().also {
|
||||||
val requestId = it.headers[OC_X_REQUEST_ID]
|
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)
|
logHeaders(requestId, it.headers, REQUEST)
|
||||||
logRequestBody(requestId, it.body)
|
logRequestBody(requestId, it.body)
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ class LogInterceptor : Interceptor {
|
|||||||
RESPONSE,
|
RESPONSE,
|
||||||
INFO,
|
INFO,
|
||||||
requestId,
|
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)
|
logHeaders(requestId, it.headers, RESPONSE)
|
||||||
logResponseBody(requestId, it.body)
|
logResponseBody(requestId, it.body)
|
||||||
|
@ -1,3 +1,27 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
package com.owncloud.android.lib.common.http.methods
|
package com.owncloud.android.lib.common.http.methods
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.http.HttpClient
|
import com.owncloud.android.lib.common.http.HttpClient
|
||||||
@ -14,23 +38,41 @@ import java.net.URL
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
abstract class HttpBaseMethod constructor(url: URL) {
|
abstract class HttpBaseMethod constructor(url: URL) {
|
||||||
var okHttpClient: OkHttpClient
|
|
||||||
var httpUrl: HttpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException()
|
var httpUrl: HttpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException()
|
||||||
var request: Request
|
var request: Request
|
||||||
|
var followPermanentRedirects = false
|
||||||
abstract var response: Response
|
abstract var response: Response
|
||||||
|
|
||||||
var call: Call? = null
|
var call: Call? = null
|
||||||
|
|
||||||
|
var followRedirects: Boolean = true
|
||||||
|
var retryOnConnectionFailure: Boolean = true
|
||||||
|
var connectionTimeoutVal: Long? = null
|
||||||
|
var connectionTimeoutUnit: TimeUnit? = null
|
||||||
|
var readTimeoutVal: Long? = null
|
||||||
|
private set
|
||||||
|
var readTimeoutUnit: TimeUnit? = null
|
||||||
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
okHttpClient = HttpClient.getOkHttpClient()
|
|
||||||
request = Request.Builder()
|
request = Request.Builder()
|
||||||
.url(httpUrl)
|
.url(httpUrl)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
open fun execute(): Int {
|
open fun execute(httpClient: HttpClient): Int {
|
||||||
return onExecute()
|
val okHttpClient = httpClient.okHttpClient.newBuilder().apply {
|
||||||
|
retryOnConnectionFailure(retryOnConnectionFailure)
|
||||||
|
followRedirects(followRedirects)
|
||||||
|
readTimeoutUnit?.let { unit ->
|
||||||
|
readTimeoutVal?.let { readTimeout(it, unit) }
|
||||||
|
}
|
||||||
|
connectionTimeoutUnit?.let { unit ->
|
||||||
|
connectionTimeoutVal?.let { connectTimeout(it, unit) }
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
return onExecute(okHttpClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun setUrl(url: HttpUrl) {
|
open fun setUrl(url: HttpUrl) {
|
||||||
@ -99,6 +141,11 @@ abstract class HttpBaseMethod constructor(url: URL) {
|
|||||||
return response.body?.byteStream()
|
return response.body?.byteStream()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the final url after following the last redirect.
|
||||||
|
*/
|
||||||
|
open fun getFinalUrl() = response.request.url
|
||||||
|
|
||||||
/*************************
|
/*************************
|
||||||
*** Connection Params ***
|
*** Connection Params ***
|
||||||
*************************/
|
*************************/
|
||||||
@ -107,31 +154,19 @@ abstract class HttpBaseMethod constructor(url: URL) {
|
|||||||
// Setter
|
// Setter
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
// Connection parameters
|
// Connection parameters
|
||||||
open fun setRetryOnConnectionFailure(retryOnConnectionFailure: Boolean) {
|
|
||||||
okHttpClient = okHttpClient.newBuilder()
|
|
||||||
.retryOnConnectionFailure(retryOnConnectionFailure)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) {
|
open fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) {
|
||||||
okHttpClient = okHttpClient.newBuilder()
|
readTimeoutVal = readTimeout
|
||||||
.readTimeout(readTimeout, timeUnit)
|
readTimeoutUnit = timeUnit
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun setConnectionTimeout(
|
open fun setConnectionTimeout(
|
||||||
connectionTimeout: Long,
|
connectionTimeout: Long,
|
||||||
timeUnit: TimeUnit
|
timeUnit: TimeUnit
|
||||||
) {
|
) {
|
||||||
okHttpClient = okHttpClient.newBuilder()
|
connectionTimeoutVal = connectionTimeout
|
||||||
.readTimeout(connectionTimeout, timeUnit)
|
connectionTimeoutUnit = timeUnit
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun setFollowRedirects(followRedirects: Boolean) {
|
|
||||||
okHttpClient = okHttpClient.newBuilder()
|
|
||||||
.followRedirects(followRedirects)
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/************
|
/************
|
||||||
@ -148,5 +183,5 @@ abstract class HttpBaseMethod constructor(url: URL) {
|
|||||||
// For override
|
// For override
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
protected abstract fun onExecute(): Int
|
protected abstract fun onExecute(okHttpClient: OkHttpClient): Int
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
@ -33,10 +34,10 @@ import java.net.URL
|
|||||||
*/
|
*/
|
||||||
class DeleteMethod(url: URL) : HttpMethod(url) {
|
class DeleteMethod(url: URL) : HttpMethod(url) {
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun onExecute(): Int {
|
override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||||
request = request.newBuilder()
|
request = request.newBuilder()
|
||||||
.delete()
|
.delete()
|
||||||
.build()
|
.build()
|
||||||
return super.onExecute()
|
return super.onExecute(okHttpClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
@ -33,10 +34,10 @@ import java.net.URL
|
|||||||
*/
|
*/
|
||||||
class GetMethod(url: URL) : HttpMethod(url) {
|
class GetMethod(url: URL) : HttpMethod(url) {
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun onExecute(): Int {
|
override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||||
request = request.newBuilder()
|
request = request.newBuilder()
|
||||||
.get()
|
.get()
|
||||||
.build()
|
.build()
|
||||||
return super.onExecute()
|
return super.onExecute(okHttpClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
|
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ abstract class HttpMethod(
|
|||||||
|
|
||||||
override lateinit var response: Response
|
override lateinit var response: Response
|
||||||
|
|
||||||
public override fun onExecute(): Int {
|
public override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||||
call = okHttpClient.newCall(request)
|
call = okHttpClient.newCall(request)
|
||||||
call?.let { response = it.execute() }
|
call?.let { response = it.execute() }
|
||||||
return super.statusCode
|
return super.statusCode
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@ -37,10 +38,10 @@ class PostMethod(
|
|||||||
private val postRequestBody: RequestBody
|
private val postRequestBody: RequestBody
|
||||||
) : HttpMethod(url) {
|
) : HttpMethod(url) {
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun onExecute(): Int {
|
override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||||
request = request.newBuilder()
|
request = request.newBuilder()
|
||||||
.post(postRequestBody)
|
.post(postRequestBody)
|
||||||
.build()
|
.build()
|
||||||
return super.onExecute()
|
return super.onExecute(okHttpClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@ -37,10 +38,10 @@ class PutMethod(
|
|||||||
private val putRequestBody: RequestBody
|
private val putRequestBody: RequestBody
|
||||||
) : HttpMethod(url) {
|
) : HttpMethod(url) {
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun onExecute(): Int {
|
override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||||
request = request.newBuilder()
|
request = request.newBuilder()
|
||||||
.put(putRequestBody)
|
.put(putRequestBody)
|
||||||
.build()
|
.build()
|
||||||
return super.onExecute()
|
return super.onExecute(okHttpClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.common.http.methods.webdav
|
package com.owncloud.android.lib.common.http.methods.webdav
|
||||||
|
|
||||||
|
import at.bitfire.dav4jvm.DavOCResource
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
@ -35,10 +36,10 @@ import java.net.URL
|
|||||||
class CopyMethod(
|
class CopyMethod(
|
||||||
val url: URL,
|
val url: URL,
|
||||||
private val destinationUrl: String,
|
private val destinationUrl: String,
|
||||||
private val forceOverride: Boolean
|
val forceOverride: Boolean = false
|
||||||
) : DavMethod(url) {
|
) : DavMethod(url) {
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
public override fun onExecute(): Int {
|
public override fun onDavExecute(davResource: DavOCResource): Int {
|
||||||
davResource.copy(
|
davResource.copy(
|
||||||
destinationUrl,
|
destinationUrl,
|
||||||
forceOverride,
|
forceOverride,
|
||||||
|
@ -29,14 +29,11 @@ import at.bitfire.dav4jvm.exception.HttpException
|
|||||||
import at.bitfire.dav4jvm.exception.RedirectException
|
import at.bitfire.dav4jvm.exception.RedirectException
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
|
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|
||||||
import okhttp3.Protocol
|
import okhttp3.Protocol
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||||
import java.net.MalformedURLException
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper to perform WebDAV (dav4android) calls
|
* Wrapper to perform WebDAV (dav4android) calls
|
||||||
@ -44,27 +41,25 @@ import java.util.concurrent.TimeUnit
|
|||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
*/
|
*/
|
||||||
abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) {
|
abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) {
|
||||||
protected var davResource: DavOCResource
|
|
||||||
|
|
||||||
override lateinit var response: Response
|
override lateinit var response: Response
|
||||||
|
private var davResource: DavOCResource? = null
|
||||||
init {
|
|
||||||
val httpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException()
|
|
||||||
davResource = DavOCResource(
|
|
||||||
okHttpClient,
|
|
||||||
httpUrl,
|
|
||||||
log
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun abort() {
|
override fun abort() {
|
||||||
davResource.cancelCall()
|
davResource?.cancelCall()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract fun onDavExecute(davResource: DavOCResource): Int
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
override fun execute(): Int {
|
override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||||
return try {
|
return try {
|
||||||
onExecute()
|
davResource = DavOCResource(
|
||||||
|
okHttpClient.newBuilder().followRedirects(false).build(),
|
||||||
|
httpUrl,
|
||||||
|
log
|
||||||
|
)
|
||||||
|
|
||||||
|
onDavExecute(davResource!!)
|
||||||
} catch (httpException: HttpException) {
|
} catch (httpException: HttpException) {
|
||||||
// Modify responses with information gathered from exceptions
|
// Modify responses with information gathered from exceptions
|
||||||
if (httpException is RedirectException) {
|
if (httpException is RedirectException) {
|
||||||
@ -91,71 +86,12 @@ abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////
|
|
||||||
// Setter
|
|
||||||
//////////////////////////////
|
|
||||||
// Connection parameters
|
|
||||||
override fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) {
|
|
||||||
super.setReadTimeout(readTimeout, timeUnit)
|
|
||||||
davResource = DavOCResource(
|
|
||||||
okHttpClient,
|
|
||||||
request.url,
|
|
||||||
log
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setConnectionTimeout(
|
|
||||||
connectionTimeout: Long,
|
|
||||||
timeUnit: TimeUnit
|
|
||||||
) {
|
|
||||||
super.setConnectionTimeout(connectionTimeout, timeUnit)
|
|
||||||
davResource = DavOCResource(
|
|
||||||
okHttpClient,
|
|
||||||
request.url,
|
|
||||||
log
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setFollowRedirects(followRedirects: Boolean) {
|
|
||||||
super.setFollowRedirects(followRedirects)
|
|
||||||
davResource = DavOCResource(
|
|
||||||
okHttpClient,
|
|
||||||
request.url,
|
|
||||||
log
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setUrl(url: HttpUrl) {
|
|
||||||
super.setUrl(url)
|
|
||||||
davResource = DavOCResource(
|
|
||||||
okHttpClient,
|
|
||||||
request.url,
|
|
||||||
log
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setRequestHeader(name: String, value: String) {
|
|
||||||
super.setRequestHeader(name, value)
|
|
||||||
davResource = DavOCResource(
|
|
||||||
okHttpClient,
|
|
||||||
request.url,
|
|
||||||
log
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
// Getter
|
// Getter
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
override fun setRetryOnConnectionFailure(retryOnConnectionFailure: Boolean) {
|
|
||||||
super.setRetryOnConnectionFailure(retryOnConnectionFailure)
|
|
||||||
davResource = DavOCResource(
|
|
||||||
okHttpClient,
|
|
||||||
request.url,
|
|
||||||
log
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isAborted: Boolean
|
override val isAborted: Boolean
|
||||||
get() = davResource.isCallAborted()
|
get() = davResource?.isCallAborted() ?: false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,36 @@
|
|||||||
package com.owncloud.android.lib.common.http.methods.webdav
|
package com.owncloud.android.lib.common.http.methods.webdav
|
||||||
|
|
||||||
import at.bitfire.dav4jvm.Property
|
import at.bitfire.dav4jvm.Property
|
||||||
import at.bitfire.dav4jvm.PropertyUtils.getAllPropSet
|
|
||||||
import at.bitfire.dav4jvm.PropertyUtils.getQuotaPropset
|
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 {
|
object DavUtils {
|
||||||
@JvmStatic val allPropset: Array<Property.Name>
|
@JvmStatic val allPropSet: Array<Property.Name>
|
||||||
get() = getAllPropSet()
|
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>
|
val quotaPropSet: Array<Property.Name>
|
||||||
get() = getQuotaPropset()
|
get() = getQuotaPropset()
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.common.http.methods.webdav
|
package com.owncloud.android.lib.common.http.methods.webdav
|
||||||
|
|
||||||
|
import at.bitfire.dav4jvm.DavOCResource
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ import java.net.URL
|
|||||||
*/
|
*/
|
||||||
class MkColMethod(url: URL) : DavMethod(url) {
|
class MkColMethod(url: URL) : DavMethod(url) {
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
public override fun onExecute(): Int {
|
public override fun onDavExecute(davResource: DavOCResource): Int {
|
||||||
davResource.mkCol(
|
davResource.mkCol(
|
||||||
xmlBody = null,
|
xmlBody = null,
|
||||||
listOfHeaders = super.getRequestHeadersAsHashMap()
|
listOfHeaders = super.getRequestHeadersAsHashMap()
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.common.http.methods.webdav
|
package com.owncloud.android.lib.common.http.methods.webdav
|
||||||
|
|
||||||
|
import at.bitfire.dav4jvm.DavOCResource
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
@ -35,10 +36,10 @@ import java.net.URL
|
|||||||
class MoveMethod(
|
class MoveMethod(
|
||||||
url: URL,
|
url: URL,
|
||||||
private val destinationUrl: String,
|
private val destinationUrl: String,
|
||||||
private val forceOverride: Boolean
|
val forceOverride: Boolean = false
|
||||||
) : DavMethod(url) {
|
) : DavMethod(url) {
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
public override fun onExecute(): Int {
|
override fun onDavExecute(davResource: DavOCResource): Int {
|
||||||
davResource.move(
|
davResource.move(
|
||||||
destinationUrl,
|
destinationUrl,
|
||||||
forceOverride,
|
forceOverride,
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.common.http.methods.webdav
|
package com.owncloud.android.lib.common.http.methods.webdav
|
||||||
|
|
||||||
|
import at.bitfire.dav4jvm.DavOCResource
|
||||||
import at.bitfire.dav4jvm.Property
|
import at.bitfire.dav4jvm.Property
|
||||||
import at.bitfire.dav4jvm.Response
|
import at.bitfire.dav4jvm.Response
|
||||||
import at.bitfire.dav4jvm.Response.HrefRelation
|
import at.bitfire.dav4jvm.Response.HrefRelation
|
||||||
@ -47,12 +48,12 @@ class PropfindMethod(
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
@Throws(IOException::class, DavException::class)
|
@Throws(IOException::class, DavException::class)
|
||||||
public override fun onExecute(): Int {
|
public override fun onDavExecute(davResource: DavOCResource): Int {
|
||||||
davResource.propfind(
|
davResource.propfind(
|
||||||
depth = depth,
|
depth = depth,
|
||||||
reqProp = *propertiesToRequest,
|
reqProp = propertiesToRequest,
|
||||||
listOfHeaders = super.getRequestHeadersAsHashMap(),
|
listOfHeaders = super.getRequestHeadersAsHashMap(),
|
||||||
callback = { response: Response, hrefRelation: HrefRelation? ->
|
callback = { response: Response, hrefRelation: HrefRelation ->
|
||||||
when (hrefRelation) {
|
when (hrefRelation) {
|
||||||
HrefRelation.MEMBER -> members.add(response)
|
HrefRelation.MEMBER -> members.add(response)
|
||||||
HrefRelation.SELF -> this.root = response
|
HrefRelation.SELF -> this.root = response
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.common.http.methods.webdav
|
package com.owncloud.android.lib.common.http.methods.webdav
|
||||||
|
|
||||||
|
import at.bitfire.dav4jvm.DavOCResource
|
||||||
import at.bitfire.dav4jvm.exception.HttpException
|
import at.bitfire.dav4jvm.exception.HttpException
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
@ -39,7 +40,7 @@ class PutMethod(
|
|||||||
private val putRequestBody: RequestBody
|
private val putRequestBody: RequestBody
|
||||||
) : DavMethod(url) {
|
) : DavMethod(url) {
|
||||||
@Throws(IOException::class, HttpException::class)
|
@Throws(IOException::class, HttpException::class)
|
||||||
public override fun onExecute(): Int {
|
public override fun onDavExecute(davResource: DavOCResource): Int {
|
||||||
davResource.put(
|
davResource.put(
|
||||||
putRequestBody,
|
putRequestBody,
|
||||||
super.getRequestHeader(HttpConstants.IF_MATCH_HEADER),
|
super.getRequestHeader(HttpConstants.IF_MATCH_HEADER),
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.common.http.methods.webdav.properties
|
||||||
|
|
||||||
|
import at.bitfire.dav4jvm.Property
|
||||||
|
import at.bitfire.dav4jvm.XmlUtils
|
||||||
|
import org.xmlpull.v1.XmlPullParser
|
||||||
|
|
||||||
|
class OCShareTypes : ShareTypeListProperty() {
|
||||||
|
|
||||||
|
class Factory : ShareTypeListProperty.Factory() {
|
||||||
|
|
||||||
|
override fun create(parser: XmlPullParser) =
|
||||||
|
create(parser, OCShareTypes())
|
||||||
|
|
||||||
|
override fun getName(): Property.Name = NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
val NAME = Property.Name(XmlUtils.NS_OWNCLOUD, "share-types")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.common.http.methods.webdav.properties
|
||||||
|
|
||||||
|
import at.bitfire.dav4jvm.Property
|
||||||
|
import at.bitfire.dav4jvm.PropertyFactory
|
||||||
|
import at.bitfire.dav4jvm.XmlUtils
|
||||||
|
import org.xmlpull.v1.XmlPullParser
|
||||||
|
import java.util.LinkedList
|
||||||
|
|
||||||
|
abstract class ShareTypeListProperty : Property {
|
||||||
|
|
||||||
|
val shareTypes = LinkedList<String>()
|
||||||
|
|
||||||
|
override fun toString() = "share types =[" + shareTypes.joinToString(", ") + "]"
|
||||||
|
|
||||||
|
abstract class Factory : PropertyFactory {
|
||||||
|
|
||||||
|
fun create(parser: XmlPullParser, list: ShareTypeListProperty): ShareTypeListProperty {
|
||||||
|
XmlUtils.readTextPropertyList(parser, Property.Name(XmlUtils.NS_OWNCLOUD, "share-type"), list.shareTypes)
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,115 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.common.network;
|
|
||||||
|
|
||||||
import okhttp3.MediaType;
|
|
||||||
import okio.BufferedSink;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Request body that represents a file chunk and include information about the progress when uploading it
|
|
||||||
*
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
public class ChunkFromFileRequestBody extends FileRequestBody {
|
|
||||||
|
|
||||||
private final FileChannel mChannel;
|
|
||||||
private final long mChunkSize;
|
|
||||||
private long mOffset;
|
|
||||||
private long mTransferred;
|
|
||||||
private ByteBuffer mBuffer = ByteBuffer.allocate(4096);
|
|
||||||
|
|
||||||
public ChunkFromFileRequestBody(File file, MediaType contentType, FileChannel channel, long chunkSize) {
|
|
||||||
super(file, contentType);
|
|
||||||
if (channel == null) {
|
|
||||||
throw new IllegalArgumentException("File may not be null");
|
|
||||||
}
|
|
||||||
if (chunkSize <= 0) {
|
|
||||||
throw new IllegalArgumentException("Chunk size must be greater than zero");
|
|
||||||
}
|
|
||||||
this.mChannel = channel;
|
|
||||||
this.mChunkSize = chunkSize;
|
|
||||||
mOffset = 0;
|
|
||||||
mTransferred = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long contentLength() {
|
|
||||||
try {
|
|
||||||
return Math.min(mChunkSize, mChannel.size() - mChannel.position());
|
|
||||||
} catch (IOException e) {
|
|
||||||
return mChunkSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(BufferedSink sink) {
|
|
||||||
int readCount;
|
|
||||||
Iterator<OnDatatransferProgressListener> it;
|
|
||||||
|
|
||||||
try {
|
|
||||||
mChannel.position(mOffset);
|
|
||||||
long size = mFile.length();
|
|
||||||
if (size == 0) {
|
|
||||||
size = -1;
|
|
||||||
}
|
|
||||||
long maxCount = Math.min(mOffset + mChunkSize, mChannel.size());
|
|
||||||
while (mChannel.position() < maxCount) {
|
|
||||||
|
|
||||||
readCount = mChannel.read(mBuffer);
|
|
||||||
|
|
||||||
sink.getBuffer().write(mBuffer.array(), 0, readCount);
|
|
||||||
|
|
||||||
sink.flush();
|
|
||||||
|
|
||||||
mBuffer.clear();
|
|
||||||
if (mTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks
|
|
||||||
mTransferred += readCount;
|
|
||||||
}
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
it = mDataTransferListeners.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
it.next().onTransferProgress(readCount, mTransferred, size, mFile.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.v("Chunk with size " + mChunkSize + " written in request body");
|
|
||||||
|
|
||||||
} catch (Exception exception) {
|
|
||||||
Timber.e(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOffset(long offset) {
|
|
||||||
this.mOffset = offset;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,92 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.common.network
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.resources.files.chunks.ChunkedUploadFromFileSystemOperation.Companion.CHUNK_SIZE
|
||||||
|
import okhttp3.MediaType
|
||||||
|
import okio.BufferedSink
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.channels.FileChannel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Request body that represents a file chunk and include information about the progress when uploading it
|
||||||
|
*
|
||||||
|
* @author David González Verdugo
|
||||||
|
*/
|
||||||
|
class ChunkFromFileRequestBody(
|
||||||
|
file: File,
|
||||||
|
contentType: MediaType?,
|
||||||
|
private val channel: FileChannel,
|
||||||
|
private val chunkSize: Long = CHUNK_SIZE
|
||||||
|
) : FileRequestBody(file, contentType) {
|
||||||
|
|
||||||
|
private var offset: Long = 0
|
||||||
|
private var alreadyTransferred: Long = 0
|
||||||
|
private val buffer = ByteBuffer.allocate(4_096)
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(chunkSize > 0) { "Chunk size must be greater than zero" }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun contentLength(): Long {
|
||||||
|
return chunkSize.coerceAtMost(channel.size() - channel.position())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeTo(sink: BufferedSink) {
|
||||||
|
var readCount: Int
|
||||||
|
var iterator: Iterator<OnDatatransferProgressListener>
|
||||||
|
try {
|
||||||
|
channel.position(offset)
|
||||||
|
|
||||||
|
val maxCount = (offset + chunkSize).coerceAtMost(channel.size())
|
||||||
|
while (channel.position() < maxCount) {
|
||||||
|
readCount = channel.read(buffer)
|
||||||
|
val bytesToWriteInBuffer = readCount.toLong().coerceAtMost(file.length() - alreadyTransferred).toInt()
|
||||||
|
sink.buffer.write(buffer.array(), 0, bytesToWriteInBuffer)
|
||||||
|
sink.flush()
|
||||||
|
buffer.clear()
|
||||||
|
|
||||||
|
if (alreadyTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks
|
||||||
|
alreadyTransferred += readCount.toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(dataTransferListeners) {
|
||||||
|
iterator = dataTransferListeners.iterator()
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
iterator.next().onTransferProgress(readCount.toLong(), alreadyTransferred, file.length(), file.absolutePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Timber.e(exception, "Transferred " + alreadyTransferred + " bytes from a total of " + file.length())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOffset(newOffset: Long) {
|
||||||
|
offset = newOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.lib.common.network
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.OpenableColumns
|
||||||
|
import okhttp3.MediaType
|
||||||
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import okio.BufferedSink
|
||||||
|
import okio.Source
|
||||||
|
import okio.source
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class ContentUriRequestBody(
|
||||||
|
private val contentResolver: ContentResolver,
|
||||||
|
private val contentUri: Uri
|
||||||
|
) : RequestBody(), ProgressiveDataTransferer {
|
||||||
|
|
||||||
|
private val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
|
||||||
|
|
||||||
|
val fileSize: Long = contentResolver.query(contentUri, null, null, null, null)?.use { cursor ->
|
||||||
|
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||||
|
cursor.moveToFirst()
|
||||||
|
cursor.getLong(sizeIndex)
|
||||||
|
} ?: -1
|
||||||
|
|
||||||
|
override fun contentType(): MediaType? {
|
||||||
|
val contentType = contentResolver.getType(contentUri) ?: return null
|
||||||
|
return contentType.toMediaTypeOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun contentLength(): Long {
|
||||||
|
return fileSize
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeTo(sink: BufferedSink) {
|
||||||
|
val inputStream = contentResolver.openInputStream(contentUri)
|
||||||
|
?: throw IOException("Couldn't open content URI for reading: $contentUri")
|
||||||
|
|
||||||
|
val previousTime = System.currentTimeMillis()
|
||||||
|
|
||||||
|
sink.writeAndUpdateProgress(inputStream.source())
|
||||||
|
inputStream.source().close()
|
||||||
|
|
||||||
|
val laterTime = System.currentTimeMillis()
|
||||||
|
|
||||||
|
Timber.d("Difference - ${laterTime - previousTime} milliseconds")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BufferedSink.writeAndUpdateProgress(source: Source) {
|
||||||
|
var iterator: Iterator<OnDatatransferProgressListener>
|
||||||
|
|
||||||
|
try {
|
||||||
|
var totalBytesRead = 0L
|
||||||
|
var read: Long
|
||||||
|
while (source.read(this.buffer, BYTES_TO_READ).also { read = it } != -1L) {
|
||||||
|
totalBytesRead += read
|
||||||
|
this.flush()
|
||||||
|
synchronized(dataTransferListeners) {
|
||||||
|
iterator = dataTransferListeners.iterator()
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
iterator.next().onTransferProgress(read, totalBytesRead, fileSize, contentUri.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||||
|
synchronized(dataTransferListeners) {
|
||||||
|
dataTransferListeners.add(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addDatatransferProgressListeners(listeners: MutableCollection<OnDatatransferProgressListener>) {
|
||||||
|
synchronized(dataTransferListeners) {
|
||||||
|
dataTransferListeners.addAll(listeners)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||||
|
synchronized(dataTransferListeners) {
|
||||||
|
dataTransferListeners.remove(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BYTES_TO_READ = 4_096L
|
||||||
|
}
|
||||||
|
}
|
@ -1,119 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.common.network;
|
|
||||||
|
|
||||||
import okhttp3.MediaType;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
import okio.BufferedSink;
|
|
||||||
import okio.Okio;
|
|
||||||
import okio.Source;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Request body that represents a file and include information about the progress when uploading it
|
|
||||||
*
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
public class FileRequestBody extends RequestBody implements ProgressiveDataTransferer {
|
|
||||||
|
|
||||||
final Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
|
|
||||||
protected File mFile;
|
|
||||||
private MediaType mContentType;
|
|
||||||
|
|
||||||
public FileRequestBody(File file, MediaType contentType) {
|
|
||||||
mFile = file;
|
|
||||||
mContentType = contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOneShot() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaType contentType() {
|
|
||||||
return mContentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long contentLength() {
|
|
||||||
return mFile.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(BufferedSink sink) {
|
|
||||||
Source source;
|
|
||||||
Iterator<OnDatatransferProgressListener> it;
|
|
||||||
try {
|
|
||||||
source = Okio.source(mFile);
|
|
||||||
|
|
||||||
long transferred = 0;
|
|
||||||
long read;
|
|
||||||
|
|
||||||
while ((read = source.read(sink.buffer(), 4096)) != -1) {
|
|
||||||
transferred += read;
|
|
||||||
sink.flush();
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
it = mDataTransferListeners.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
it.next().onTransferProgress(read, transferred, mFile.length(), mFile.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("File with name " + mFile.getName() + " and size " + mFile.length() + " written in request body");
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Timber.e(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
mDataTransferListeners.add(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners) {
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
mDataTransferListeners.addAll(listeners);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
mDataTransferListeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,97 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.common.network
|
||||||
|
|
||||||
|
import okhttp3.MediaType
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import okio.BufferedSink
|
||||||
|
import okio.Source
|
||||||
|
import okio.source
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
import java.util.HashSet
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Request body that represents a file and include information about the progress when uploading it
|
||||||
|
*
|
||||||
|
* @author David González Verdugo
|
||||||
|
*/
|
||||||
|
open class FileRequestBody(
|
||||||
|
val file: File,
|
||||||
|
private val contentType: MediaType?,
|
||||||
|
) : RequestBody(), ProgressiveDataTransferer {
|
||||||
|
|
||||||
|
val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
|
||||||
|
|
||||||
|
override fun isOneShot(): Boolean = true
|
||||||
|
|
||||||
|
override fun contentType(): MediaType? = contentType
|
||||||
|
|
||||||
|
override fun contentLength(): Long = file.length()
|
||||||
|
|
||||||
|
override fun writeTo(sink: BufferedSink) {
|
||||||
|
val source: Source
|
||||||
|
var it: Iterator<OnDatatransferProgressListener>
|
||||||
|
try {
|
||||||
|
source = file.source()
|
||||||
|
var transferred: Long = 0
|
||||||
|
var read: Long
|
||||||
|
while (source.read(sink.buffer, BYTES_TO_READ).also { read = it } != -1L) {
|
||||||
|
transferred += read
|
||||||
|
sink.flush()
|
||||||
|
synchronized(dataTransferListeners) {
|
||||||
|
it = dataTransferListeners.iterator()
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next().onTransferProgress(read, transferred, file.length(), file.absolutePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Timber.d("File with name ${file.name} and size ${file.length()} written in request body")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||||
|
synchronized(dataTransferListeners) {
|
||||||
|
dataTransferListeners.add(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addDatatransferProgressListeners(listeners: Collection<OnDatatransferProgressListener>) {
|
||||||
|
synchronized(dataTransferListeners) {
|
||||||
|
dataTransferListeners.addAll(listeners)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||||
|
synchronized(dataTransferListeners) {
|
||||||
|
dataTransferListeners.remove(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BYTES_TO_READ = 4_096L
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,27 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
package com.owncloud.android.lib.common.operations;
|
package com.owncloud.android.lib.common.operations;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
@ -135,7 +159,7 @@ public abstract class RemoteOperation<T> implements Runnable {
|
|||||||
if (mAccount != null && mContext != null) {
|
if (mAccount != null && mContext != null) {
|
||||||
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
|
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
|
||||||
mClient = SingleSessionManager.getDefaultSingleton().
|
mClient = SingleSessionManager.getDefaultSingleton().
|
||||||
getClientFor(ocAccount, mContext);
|
getClientFor(ocAccount, mContext, SingleSessionManager.getConnectionValidator());
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Trying to run a remote operation " +
|
throw new IllegalStateException("Trying to run a remote operation " +
|
||||||
"asynchronously with no client and no chance to create one (no account)");
|
"asynchronously with no client and no chance to create one (no account)");
|
||||||
@ -265,4 +289,4 @@ public abstract class RemoteOperation<T> implements Runnable {
|
|||||||
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
|
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
/* ownCloud Android Library is available under MIT license
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -34,6 +34,7 @@ import com.owncloud.android.lib.common.http.HttpConstants;
|
|||||||
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
|
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
|
||||||
import com.owncloud.android.lib.common.network.CertificateCombinedException;
|
import com.owncloud.android.lib.common.network.CertificateCombinedException;
|
||||||
import okhttp3.Headers;
|
import okhttp3.Headers;
|
||||||
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.ProtocolException;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@ -59,13 +61,15 @@ public class RemoteOperationResult<T>
|
|||||||
* Generated - should be refreshed every time the class changes!!
|
* Generated - should be refreshed every time the class changes!!
|
||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = 4968939884332372230L;
|
private static final long serialVersionUID = 4968939884332372230L;
|
||||||
|
private static final String LOCATION = "location";
|
||||||
|
private static final String WWW_AUTHENTICATE = "www-authenticate";
|
||||||
|
|
||||||
private boolean mSuccess = false;
|
private boolean mSuccess = false;
|
||||||
private int mHttpCode = -1;
|
private int mHttpCode = -1;
|
||||||
private String mHttpPhrase = null;
|
private String mHttpPhrase = null;
|
||||||
private Exception mException = null;
|
private Exception mException = null;
|
||||||
private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
|
private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
|
||||||
private String mRedirectedLocation;
|
private String mRedirectedLocation = "";
|
||||||
private List<String> mAuthenticate = new ArrayList<>();
|
private List<String> mAuthenticate = new ArrayList<>();
|
||||||
private String mLastPermanentLocation = null;
|
private String mLastPermanentLocation = null;
|
||||||
private T mData = null;
|
private T mData = null;
|
||||||
@ -112,6 +116,14 @@ public class RemoteOperationResult<T>
|
|||||||
*/
|
*/
|
||||||
public RemoteOperationResult(Exception e) {
|
public RemoteOperationResult(Exception e) {
|
||||||
mException = e;
|
mException = e;
|
||||||
|
//TODO: Do propper exception handling and remove this
|
||||||
|
Timber.e("---------------------------------" +
|
||||||
|
"\nCreate RemoteOperationResult from exception." +
|
||||||
|
"\n Message: %s" +
|
||||||
|
"\n Stacktrace: %s" +
|
||||||
|
"\n---------------------------------",
|
||||||
|
ExceptionUtils.getMessage(e),
|
||||||
|
ExceptionUtils.getStackTrace(e));
|
||||||
|
|
||||||
if (e instanceof OperationCancelledException) {
|
if (e instanceof OperationCancelledException) {
|
||||||
mCode = ResultCode.CANCELLED;
|
mCode = ResultCode.CANCELLED;
|
||||||
@ -155,7 +167,10 @@ public class RemoteOperationResult<T>
|
|||||||
} else if (e instanceof FileNotFoundException) {
|
} else if (e instanceof FileNotFoundException) {
|
||||||
mCode = ResultCode.LOCAL_FILE_NOT_FOUND;
|
mCode = ResultCode.LOCAL_FILE_NOT_FOUND;
|
||||||
|
|
||||||
} else {
|
} else if (e instanceof ProtocolException) {
|
||||||
|
mCode = ResultCode.NETWORK_ERROR;
|
||||||
|
}
|
||||||
|
else {
|
||||||
mCode = ResultCode.UNKNOWN_ERROR;
|
mCode = ResultCode.UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,6 +241,10 @@ public class RemoteOperationResult<T>
|
|||||||
httpMethod.getResponseBodyAsString(),
|
httpMethod.getResponseBodyAsString(),
|
||||||
ResultCode.SPECIFIC_METHOD_NOT_ALLOWED
|
ResultCode.SPECIFIC_METHOD_NOT_ALLOWED
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
|
case HttpConstants.HTTP_TOO_EARLY:
|
||||||
|
mCode = ResultCode.TOO_EARLY;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -248,11 +267,11 @@ public class RemoteOperationResult<T>
|
|||||||
this(httpCode, httpPhrase);
|
this(httpCode, httpPhrase);
|
||||||
if (headers != null) {
|
if (headers != null) {
|
||||||
for (Map.Entry<String, List<String>> header : headers.toMultimap().entrySet()) {
|
for (Map.Entry<String, List<String>> header : headers.toMultimap().entrySet()) {
|
||||||
if ("location".equals(header.getKey().toLowerCase())) {
|
if (LOCATION.equalsIgnoreCase(header.getKey())) {
|
||||||
mRedirectedLocation = header.getValue().get(0);
|
mRedirectedLocation = header.getValue().get(0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ("www-authenticate".equals(header.getKey().toLowerCase())) {
|
if (WWW_AUTHENTICATE.equalsIgnoreCase(header.getKey())) {
|
||||||
for (String value: header.getValue()) {
|
for (String value: header.getValue()) {
|
||||||
mAuthenticate.add(value.toLowerCase());
|
mAuthenticate.add(value.toLowerCase());
|
||||||
}
|
}
|
||||||
@ -321,7 +340,7 @@ public class RemoteOperationResult<T>
|
|||||||
mHttpPhrase = errorMessage;
|
mHttpPhrase = errorMessage;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Timber.w("Error reading exception from server: %s", e.getMessage());
|
Timber.w("Error reading exception from server: %s\nTrace: %s", e.getMessage(), ExceptionUtils.getStackTrace(e));
|
||||||
// mCode stays as set in this(success, httpCode, headers)
|
// mCode stays as set in this(success, httpCode, headers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -572,6 +591,8 @@ public class RemoteOperationResult<T>
|
|||||||
SPECIFIC_SERVICE_UNAVAILABLE,
|
SPECIFIC_SERVICE_UNAVAILABLE,
|
||||||
SPECIFIC_UNSUPPORTED_MEDIA_TYPE,
|
SPECIFIC_UNSUPPORTED_MEDIA_TYPE,
|
||||||
SPECIFIC_METHOD_NOT_ALLOWED,
|
SPECIFIC_METHOD_NOT_ALLOWED,
|
||||||
SPECIFIC_BAD_REQUEST
|
SPECIFIC_BAD_REQUEST,
|
||||||
|
TOO_EARLY,
|
||||||
|
NETWORK_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,20 +22,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.shares;
|
package com.owncloud.android.lib.common.utils
|
||||||
|
|
||||||
/**
|
fun Any.isOneOf(vararg values: Any): Boolean {
|
||||||
* Contains Constants for Share Operation
|
return this in values
|
||||||
*
|
|
||||||
* @author masensio
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class ShareUtils {
|
|
||||||
|
|
||||||
// OCS Route
|
|
||||||
public static final String SHARING_API_PATH = "ocs/v2.php/apps/files_sharing/api/v1/shares";
|
|
||||||
|
|
||||||
// String to build the link with the token of a share:
|
|
||||||
public static final String SHARING_LINK_PATH = "/index.php/s/";
|
|
||||||
}
|
}
|
@ -1,3 +1,27 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
package com.owncloud.android.lib.common.utils
|
package com.owncloud.android.lib.common.utils
|
||||||
|
|
||||||
import info.hannes.timber.FileLoggingTree
|
import info.hannes.timber.FileLoggingTree
|
||||||
@ -9,7 +33,7 @@ object LoggingHelper {
|
|||||||
|
|
||||||
fun startLogging(directory: File, storagePath: String) {
|
fun startLogging(directory: File, storagePath: String) {
|
||||||
fileLoggingTree()?.let {
|
fileLoggingTree()?.let {
|
||||||
Timber.forest().drop(Timber.forest().indexOf(it))
|
Timber.uproot(it)
|
||||||
}
|
}
|
||||||
if (!directory.exists())
|
if (!directory.exists())
|
||||||
directory.mkdirs()
|
directory.mkdirs()
|
||||||
@ -18,7 +42,7 @@ object LoggingHelper {
|
|||||||
|
|
||||||
fun stopLogging() {
|
fun stopLogging() {
|
||||||
fileLoggingTree()?.let {
|
fileLoggingTree()?.let {
|
||||||
Timber.forest().drop(Timber.forest().indexOf(it))
|
Timber.uproot(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ data class CommonOcsResponse<T>(
|
|||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class OCSResponse<T>(
|
data class OCSResponse<T>(
|
||||||
val meta: MetaData,
|
val meta: MetaData,
|
||||||
val data: T
|
val data: T?
|
||||||
)
|
)
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
|
@ -1,21 +1,27 @@
|
|||||||
/**
|
/* ownCloud Android Library is available under MIT license
|
||||||
* ownCloud Android client application
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
*
|
*
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* it under the terms of the GNU General Public License version 2,
|
* all copies or substantial portions of the Software.
|
||||||
* as published by the Free Software Foundation.
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
* <p>
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources
|
package com.owncloud.android.lib.resources
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
/* 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.JsonAdapter
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import okhttp3.FormBody
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class GetUrlToOpenInWebRemoteOperation(
|
||||||
|
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, appName).toRequestBody()
|
||||||
|
|
||||||
|
val stringUrl =
|
||||||
|
client.baseUri.toString() + WebdavUtils.encodePath(openWithWebEndpoint)
|
||||||
|
|
||||||
|
val postMethod = PostMethod(URL(stringUrl), openInWebRequestBody).apply {
|
||||||
|
setReadTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
setConnectionTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(postMethod)
|
||||||
|
Timber.d("Open in web for file: $fileId - $status${if (!isSuccess(status)) "(FAIL)" else ""}")
|
||||||
|
|
||||||
|
if (isSuccess(status)) RemoteOperationResult<String>(ResultCode.OK).apply {
|
||||||
|
val moshi = Moshi.Builder().build()
|
||||||
|
val adapter: JsonAdapter<OpenInWebResponse> = moshi.adapter(OpenInWebResponse::class.java)
|
||||||
|
|
||||||
|
data = postMethod.getResponseBodyAsString()?.let { adapter.fromJson(it)!!.uri }
|
||||||
|
}
|
||||||
|
else RemoteOperationResult<String>(postMethod).apply { data = "" }
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val result = RemoteOperationResult<String>(e)
|
||||||
|
Timber.e(e, "Open in web for file: $fileId failed")
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
|
||||||
|
|
||||||
|
data class OpenInWebParams(
|
||||||
|
val fileId: String,
|
||||||
|
val appName: String,
|
||||||
|
) {
|
||||||
|
fun toRequestBody(): RequestBody =
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class OpenInWebResponse(val uri: String)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Maximum time to wait for a response from the server in milliseconds.
|
||||||
|
*/
|
||||||
|
private const val TIMEOUT = 5_000L
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
/* ownCloud Android Library is available under MIT license
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* @author Abel García de Prada
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 ownCloud GmbH.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,25 +23,34 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
package com.owncloud.android.lib.resources.appregistry.responses
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files.chunks;
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation;
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class AppRegistryResponse(
|
||||||
|
@Json(name = "mime-types")
|
||||||
|
val value: List<AppRegistryMimeTypeResponse>
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
@JsonClass(generateAdapter = true)
|
||||||
* Remote operation performing the creation of a new folder to save chunks during an upload to the ownCloud server.
|
data class AppRegistryMimeTypeResponse(
|
||||||
*
|
@Json(name = "mime_type") val mimeType: String,
|
||||||
* @author David González Verdugo
|
val ext: String? = null,
|
||||||
*/
|
@Json(name = "app_providers")
|
||||||
public class CreateRemoteChunkFolderOperation extends CreateRemoteFolderOperation {
|
val appProviders: List<AppRegistryProviderResponse>,
|
||||||
/**
|
val name: String? = null,
|
||||||
* Constructor
|
val icon: String? = null,
|
||||||
*
|
val description: String? = null,
|
||||||
* @param remotePath Full path to the new directory to create in the remote server.
|
@Json(name = "allow_creation")
|
||||||
* @param createFullPath 'True' means that all the ancestor folders should be created.
|
val allowCreation: Boolean? = null,
|
||||||
*/
|
@Json(name = "default_application")
|
||||||
public CreateRemoteChunkFolderOperation(String remotePath, boolean createFullPath) {
|
val defaultApplication: String? = null
|
||||||
super(remotePath, createFullPath);
|
)
|
||||||
createChunksFolder = true;
|
|
||||||
}
|
@JsonClass(generateAdapter = true)
|
||||||
}
|
data class AppRegistryProviderResponse(
|
||||||
|
val name: String,
|
||||||
|
val icon: String,
|
||||||
|
)
|
@ -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>
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
/* ownCloud Android Library is available under MIT license
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* Copyright (C) 2023 ownCloud GmbH.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -25,9 +25,8 @@ package com.owncloud.android.lib.resources.files
|
|||||||
|
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils.allPropset
|
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils.allPropSet
|
||||||
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
|
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
|
||||||
import com.owncloud.android.lib.common.network.RedirectionPath
|
|
||||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
@ -42,41 +41,29 @@ import java.util.concurrent.TimeUnit
|
|||||||
* @author David A. Velasco
|
* @author David A. Velasco
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
* @author Abel García de Prada
|
* @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 remotePath Path to append to the URL owned by the client instance.
|
||||||
* @param isUserLogged When `true`, the username won't be added at the end of the PROPFIND url since is not
|
* @param isUserLoggedIn When `true`, the username won't be added at the end of the PROPFIND url since is not
|
||||||
* needed to check user credentials
|
* needed to check user credentials
|
||||||
*/
|
*/
|
||||||
class CheckPathExistenceRemoteOperation(
|
class CheckPathExistenceRemoteOperation(
|
||||||
val remotePath: String? = "",
|
val remotePath: String? = "",
|
||||||
val isUserLogged: Boolean
|
val isUserLoggedIn: Boolean,
|
||||||
|
val spaceWebDavUrl: String? = null,
|
||||||
) : RemoteOperation<Boolean>() {
|
) : RemoteOperation<Boolean>() {
|
||||||
/**
|
|
||||||
* Gets the sequence of redirections followed during the execution of the operation.
|
|
||||||
*
|
|
||||||
* @return Sequence of redirections followed, if any, or NULL if the operation was not executed.
|
|
||||||
*/
|
|
||||||
var redirectionPath: RedirectionPath? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> {
|
override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> {
|
||||||
val previousFollowRedirects = client.followRedirects()
|
val baseStringUrl = spaceWebDavUrl ?: if (isUserLoggedIn) client.userFilesWebDavUri.toString() else client.baseFilesWebDavUri.toString()
|
||||||
return try {
|
val stringUrl = if (isUserLoggedIn) baseStringUrl + WebdavUtils.encodePath(remotePath) else baseStringUrl
|
||||||
val stringUrl =
|
|
||||||
if (isUserLogged) client.baseFilesWebDavUri.toString()
|
|
||||||
else client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)
|
|
||||||
|
|
||||||
val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropset).apply {
|
return try {
|
||||||
|
val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropSet).apply {
|
||||||
setReadTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
|
setReadTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
|
||||||
setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
|
setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
client.setFollowRedirects(false)
|
val status = client.executeHttpMethod(propFindMethod)
|
||||||
var status = client.executeHttpMethod(propFindMethod)
|
|
||||||
if (previousFollowRedirects) {
|
|
||||||
redirectionPath = client.followRedirection(propFindMethod)
|
|
||||||
status = redirectionPath?.lastStatus!!
|
|
||||||
}
|
|
||||||
/* PROPFIND method
|
/* PROPFIND method
|
||||||
* 404 NOT FOUND: path doesn't exist,
|
* 404 NOT FOUND: path doesn't exist,
|
||||||
* 207 MULTI_STATUS: path exists.
|
* 207 MULTI_STATUS: path exists.
|
||||||
@ -91,19 +78,13 @@ class CheckPathExistenceRemoteOperation(
|
|||||||
val result = RemoteOperationResult<Boolean>(e)
|
val result = RemoteOperationResult<Boolean>(e)
|
||||||
Timber.e(
|
Timber.e(
|
||||||
e,
|
e,
|
||||||
"Existence check for ${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)} : ${result.logMessage}"
|
"Existence check for $stringUrl : ${result.logMessage}"
|
||||||
)
|
)
|
||||||
|
result.data = false
|
||||||
result
|
result
|
||||||
} finally {
|
|
||||||
client.setFollowRedirects(previousFollowRedirects)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 'True' if the operation was executed and at least one redirection was followed.
|
|
||||||
*/
|
|
||||||
fun wasRedirected() = redirectionPath?.redirectionsCount ?: 0 > 0
|
|
||||||
|
|
||||||
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
|
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -1,129 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.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.CopyMethod;
|
|
||||||
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 timber.log.Timber;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* Allows renaming the moving file/folder at the same time.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author Christian Schabesberger
|
|
||||||
* @author David González V.
|
|
||||||
*/
|
|
||||||
public class CopyRemoteFileOperation extends RemoteOperation<String> {
|
|
||||||
|
|
||||||
private static final int COPY_READ_TIMEOUT = 600000;
|
|
||||||
private static final int COPY_CONNECTION_TIMEOUT = 5000;
|
|
||||||
|
|
||||||
private String mSrcRemotePath;
|
|
||||||
private String mTargetRemotePath;
|
|
||||||
|
|
||||||
private boolean mOverwrite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
* <p/>
|
|
||||||
* TODO Paths should finish in "/" in the case of folders. ?
|
|
||||||
*
|
|
||||||
* @param srcRemotePath Remote path of the file/folder to move.
|
|
||||||
* @param targetRemotePath Remove path desired for the file/folder after moving it.
|
|
||||||
*/
|
|
||||||
public CopyRemoteFileOperation(String srcRemotePath, String targetRemotePath, boolean overwrite
|
|
||||||
) {
|
|
||||||
mSrcRemotePath = srcRemotePath;
|
|
||||||
mTargetRemotePath = targetRemotePath;
|
|
||||||
mOverwrite = overwrite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the rename operation.
|
|
||||||
*
|
|
||||||
* @param client Client object to communicate with the remote ownCloud server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult<String> run(OwnCloudClient client) {
|
|
||||||
|
|
||||||
if (mTargetRemotePath.equals(mSrcRemotePath)) {
|
|
||||||
// nothing to do!
|
|
||||||
return new RemoteOperationResult<>(ResultCode.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mTargetRemotePath.startsWith(mSrcRemotePath)) {
|
|
||||||
return new RemoteOperationResult<>(ResultCode.INVALID_COPY_INTO_DESCENDANT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// perform remote operation
|
|
||||||
RemoteOperationResult result;
|
|
||||||
try {
|
|
||||||
CopyMethod copyMethod =
|
|
||||||
new CopyMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mSrcRemotePath)),
|
|
||||||
client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath),
|
|
||||||
mOverwrite);
|
|
||||||
|
|
||||||
copyMethod.setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
copyMethod.setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
final int status = client.executeHttpMethod(copyMethod);
|
|
||||||
|
|
||||||
if (status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT) {
|
|
||||||
String fileRemoteId = copyMethod.getResponseHeader(HttpConstants.OC_FILE_REMOTE_ID);
|
|
||||||
result = new RemoteOperationResult<>(ResultCode.OK);
|
|
||||||
result.setData(fileRemoteId);
|
|
||||||
} else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !mOverwrite) {
|
|
||||||
result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
|
|
||||||
client.exhaustResponse(copyMethod.getResponseBodyAsStream());
|
|
||||||
|
|
||||||
/// for other errors that could be explicitly handled, check first:
|
|
||||||
/// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
|
|
||||||
} else {
|
|
||||||
|
|
||||||
result = new RemoteOperationResult<>(copyMethod);
|
|
||||||
client.exhaustResponse(copyMethod.getResponseBodyAsStream());
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.i("Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
result = new RemoteOperationResult<>(e);
|
|
||||||
Timber.e(e, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,133 @@
|
|||||||
|
/* 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.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.CopyMethod
|
||||||
|
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.common.utils.isOneOf
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote operation copying a remote file or folder in the ownCloud server to a different folder
|
||||||
|
* in the same account.
|
||||||
|
*
|
||||||
|
* Allows renaming the copying file/folder at the same time.
|
||||||
|
*
|
||||||
|
* @author David A. Velasco
|
||||||
|
* @author Christian Schabesberger
|
||||||
|
* @author David González V.
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
class CopyRemoteFileOperation(
|
||||||
|
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 == sourceRemotePath && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) {
|
||||||
|
// nothing to do!
|
||||||
|
return RemoteOperationResult(ResultCode.OK)
|
||||||
|
}
|
||||||
|
if (targetRemotePath.startsWith(sourceRemotePath) && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) {
|
||||||
|
return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// perform remote operation
|
||||||
|
var result: RemoteOperationResult<String>
|
||||||
|
try {
|
||||||
|
val copyMethod = CopyMethod(
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
val status = client.executeHttpMethod(copyMethod)
|
||||||
|
when {
|
||||||
|
isSuccess(status) -> {
|
||||||
|
val fileRemoteId = copyMethod.getResponseHeader(HttpConstants.OC_FILE_REMOTE_ID)
|
||||||
|
result = RemoteOperationResult(ResultCode.OK)
|
||||||
|
result.setData(fileRemoteId)
|
||||||
|
}
|
||||||
|
|
||||||
|
isPreconditionFailed(status) -> {
|
||||||
|
result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE)
|
||||||
|
client.exhaustResponse(copyMethod.getResponseBodyAsStream())
|
||||||
|
|
||||||
|
/// 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 $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
result = RemoteOperationResult(e)
|
||||||
|
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
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
@ -1,113 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2019 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files;
|
|
||||||
|
|
||||||
import 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.webdav.MkColMethod;
|
|
||||||
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 timber.log.Timber;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote operation performing the creation of a new folder in the ownCloud server.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author masensio
|
|
||||||
*/
|
|
||||||
public class CreateRemoteFolderOperation extends RemoteOperation {
|
|
||||||
|
|
||||||
private static final int READ_TIMEOUT = 30000;
|
|
||||||
private static final int CONNECTION_TIMEOUT = 5000;
|
|
||||||
|
|
||||||
private String mRemotePath;
|
|
||||||
private boolean mCreateFullPath;
|
|
||||||
protected boolean createChunksFolder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param remotePath Full path to the new directory to create in the remote server.
|
|
||||||
* @param createFullPath 'True' means that all the ancestor folders should be created.
|
|
||||||
*/
|
|
||||||
public CreateRemoteFolderOperation(String remotePath, boolean createFullPath) {
|
|
||||||
mRemotePath = remotePath;
|
|
||||||
mCreateFullPath = createFullPath;
|
|
||||||
createChunksFolder = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the operation
|
|
||||||
*
|
|
||||||
* @param client Client object to communicate with the remote ownCloud server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult run(OwnCloudClient client) {
|
|
||||||
RemoteOperationResult result = createFolder(client);
|
|
||||||
if (!result.isSuccess() && mCreateFullPath &&
|
|
||||||
RemoteOperationResult.ResultCode.CONFLICT == result.getCode()) {
|
|
||||||
result = createParentFolder(FileUtils.getParentPath(mRemotePath), client);
|
|
||||||
if (result.isSuccess()) {
|
|
||||||
result = createFolder(client); // second (and last) try
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RemoteOperationResult createFolder(OwnCloudClient client) {
|
|
||||||
RemoteOperationResult result;
|
|
||||||
try {
|
|
||||||
Uri webDavUri = createChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri();
|
|
||||||
final MkColMethod mkcol = new MkColMethod(new URL(webDavUri + WebdavUtils.encodePath(mRemotePath)));
|
|
||||||
mkcol.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
mkcol.setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
final int status = client.executeHttpMethod(mkcol);
|
|
||||||
|
|
||||||
result = (status == HttpConstants.HTTP_CREATED)
|
|
||||||
? new RemoteOperationResult<>(ResultCode.OK)
|
|
||||||
: new RemoteOperationResult<>(mkcol);
|
|
||||||
Timber.d("Create directory " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
client.exhaustResponse(mkcol.getResponseBodyAsStream());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
result = new RemoteOperationResult<>(e);
|
|
||||||
Timber.e(e, "Create directory " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RemoteOperationResult createParentFolder(String parentPath, OwnCloudClient client) {
|
|
||||||
RemoteOperation operation = new CreateRemoteFolderOperation(parentPath, mCreateFullPath);
|
|
||||||
return operation.execute(client);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,110 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2020 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.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.MkColMethod
|
||||||
|
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 timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote operation performing the creation of a new folder in the ownCloud server.
|
||||||
|
*
|
||||||
|
* @author David A. Velasco
|
||||||
|
* @author masensio
|
||||||
|
*
|
||||||
|
* @param remotePath Full path to the new directory to create in the remote server.
|
||||||
|
* @param createFullPath 'True' means that all the ancestor folders should be created.
|
||||||
|
*/
|
||||||
|
class CreateRemoteFolderOperation(
|
||||||
|
val remotePath: String,
|
||||||
|
private val createFullPath: Boolean,
|
||||||
|
private val isChunksFolder: Boolean = false,
|
||||||
|
val spaceWebDavUrl: String? = null,
|
||||||
|
) : RemoteOperation<Unit>() {
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
|
||||||
|
var result = createFolder(client)
|
||||||
|
if (!result.isSuccess && createFullPath && result.code == ResultCode.CONFLICT) {
|
||||||
|
result = createParentFolder(FileUtils.getParentPath(remotePath), client)
|
||||||
|
|
||||||
|
if (result.isSuccess) {
|
||||||
|
// Second and last try
|
||||||
|
result = createFolder(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createFolder(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
var result: RemoteOperationResult<Unit>
|
||||||
|
try {
|
||||||
|
val webDavUri = if (isChunksFolder) {
|
||||||
|
client.uploadsWebDavUri.toString()
|
||||||
|
} else {
|
||||||
|
spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
val mkCol = MkColMethod(
|
||||||
|
URL(webDavUri + WebdavUtils.encodePath(remotePath))
|
||||||
|
).apply {
|
||||||
|
setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(mkCol)
|
||||||
|
result =
|
||||||
|
if (status == HttpConstants.HTTP_CREATED) {
|
||||||
|
RemoteOperationResult(ResultCode.OK)
|
||||||
|
} else {
|
||||||
|
RemoteOperationResult(mkCol)
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.d("Create directory $remotePath: ${result.logMessage}")
|
||||||
|
client.exhaustResponse(mkCol.getResponseBodyAsStream())
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
result = RemoteOperationResult(e)
|
||||||
|
Timber.e(e, "Create directory $remotePath: ${result.logMessage}")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createParentFolder(parentPath: String, client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
val operation: RemoteOperation<Unit> = CreateRemoteFolderOperation(parentPath, createFullPath)
|
||||||
|
return operation.execute(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val READ_TIMEOUT: Long = 30_000
|
||||||
|
private const val CONNECTION_TIMEOUT: Long = 5_000
|
||||||
|
}
|
||||||
|
}
|
@ -1,219 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2016 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files;
|
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants;
|
|
||||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod;
|
|
||||||
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
|
|
||||||
import com.owncloud.android.lib.common.network.WebdavUtils;
|
|
||||||
import com.owncloud.android.lib.common.operations.OperationCancelledException;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperation;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote operation performing the download of a remote file in the ownCloud server.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author masensio
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class DownloadRemoteFileOperation extends RemoteOperation {
|
|
||||||
|
|
||||||
private static final int FORBIDDEN_ERROR = 403;
|
|
||||||
private static final int SERVICE_UNAVAILABLE_ERROR = 503;
|
|
||||||
private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
|
|
||||||
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
|
|
||||||
private long mModificationTimestamp = 0;
|
|
||||||
private String mEtag = "";
|
|
||||||
private GetMethod mGet;
|
|
||||||
|
|
||||||
private String mRemotePath;
|
|
||||||
private String mLocalFolderPath;
|
|
||||||
|
|
||||||
public DownloadRemoteFileOperation(String remotePath, String localFolderPath) {
|
|
||||||
mRemotePath = remotePath;
|
|
||||||
mLocalFolderPath = localFolderPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult run(OwnCloudClient client) {
|
|
||||||
RemoteOperationResult result;
|
|
||||||
|
|
||||||
/// download will be performed to a temporal file, then moved to the final location
|
|
||||||
File tmpFile = new File(getTmpPath());
|
|
||||||
|
|
||||||
/// perform the download
|
|
||||||
try {
|
|
||||||
tmpFile.getParentFile().mkdirs();
|
|
||||||
result = downloadFile(client, tmpFile);
|
|
||||||
Timber.i("Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
result = new RemoteOperationResult<>(e);
|
|
||||||
Timber.e(e, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RemoteOperationResult downloadFile(OwnCloudClient client, File targetFile) throws
|
|
||||||
Exception {
|
|
||||||
|
|
||||||
RemoteOperationResult result;
|
|
||||||
int status;
|
|
||||||
boolean savedFile = false;
|
|
||||||
mGet = new GetMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)));
|
|
||||||
Iterator<OnDatatransferProgressListener> it;
|
|
||||||
|
|
||||||
FileOutputStream fos = null;
|
|
||||||
BufferedInputStream bis = null;
|
|
||||||
try {
|
|
||||||
status = client.executeHttpMethod(mGet);
|
|
||||||
if (isSuccess(status)) {
|
|
||||||
targetFile.createNewFile();
|
|
||||||
bis = new BufferedInputStream(mGet.getResponseBodyAsStream());
|
|
||||||
fos = new FileOutputStream(targetFile);
|
|
||||||
long transferred = 0;
|
|
||||||
|
|
||||||
String contentLength = mGet.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER);
|
|
||||||
long totalToTransfer =
|
|
||||||
(contentLength != null
|
|
||||||
&& contentLength.length() > 0)
|
|
||||||
? Long.parseLong(contentLength)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
byte[] bytes = new byte[4096];
|
|
||||||
int readResult;
|
|
||||||
while ((readResult = bis.read(bytes)) != -1) {
|
|
||||||
synchronized (mCancellationRequested) {
|
|
||||||
if (mCancellationRequested.get()) {
|
|
||||||
mGet.abort();
|
|
||||||
throw new OperationCancelledException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fos.write(bytes, 0, readResult);
|
|
||||||
transferred += readResult;
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
it = mDataTransferListeners.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
it.next().onTransferProgress(readResult, transferred, totalToTransfer,
|
|
||||||
targetFile.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (transferred == totalToTransfer) { // Check if the file is completed
|
|
||||||
savedFile = true;
|
|
||||||
final String modificationTime =
|
|
||||||
mGet.getResponseHeaders().get("Last-Modified") != null
|
|
||||||
? mGet.getResponseHeaders().get("Last-Modified")
|
|
||||||
: mGet.getResponseHeader("last-modified");
|
|
||||||
|
|
||||||
if (modificationTime != null) {
|
|
||||||
final Date d = WebdavUtils.parseResponseDate(modificationTime);
|
|
||||||
mModificationTimestamp = (d != null) ? d.getTime() : 0;
|
|
||||||
} else {
|
|
||||||
Timber.e("Could not read modification time from response downloading %s", mRemotePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
mEtag = WebdavUtils.getEtagFromResponse(mGet);
|
|
||||||
|
|
||||||
// Get rid of extra quotas
|
|
||||||
mEtag = mEtag.replace("\"", "");
|
|
||||||
|
|
||||||
if (mEtag.length() == 0) {
|
|
||||||
Timber.e("Could not read eTag from response downloading %s", mRemotePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
client.exhaustResponse(mGet.getResponseBodyAsStream());
|
|
||||||
// TODO some kind of error control!
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (status != FORBIDDEN_ERROR && status != SERVICE_UNAVAILABLE_ERROR) {
|
|
||||||
client.exhaustResponse(mGet.getResponseBodyAsStream());
|
|
||||||
|
|
||||||
} // else, body read by RemoteOperationResult constructor
|
|
||||||
|
|
||||||
result = isSuccess(status)
|
|
||||||
? new RemoteOperationResult<>(RemoteOperationResult.ResultCode.OK)
|
|
||||||
: new RemoteOperationResult<>(mGet);
|
|
||||||
} finally {
|
|
||||||
if (fos != null) {
|
|
||||||
fos.close();
|
|
||||||
}
|
|
||||||
if (bis != null) {
|
|
||||||
bis.close();
|
|
||||||
}
|
|
||||||
if (!savedFile && targetFile.exists()) {
|
|
||||||
targetFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSuccess(int status) {
|
|
||||||
return (status == HttpConstants.HTTP_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getTmpPath() {
|
|
||||||
return mLocalFolderPath + mRemotePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
mDataTransferListeners.add(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
mDataTransferListeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancel() {
|
|
||||||
mCancellationRequested.set(true); // atomic set; there is no need of synchronizing it
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getModificationTimestamp() {
|
|
||||||
return mModificationTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEtag() {
|
|
||||||
return mEtag;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,185 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2020 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.resources.files
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
|
||||||
|
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener
|
||||||
|
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||||
|
import com.owncloud.android.lib.common.operations.OperationCancelledException
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote operation performing the download of a remote file in the ownCloud server.
|
||||||
|
*
|
||||||
|
* @author David A. Velasco
|
||||||
|
* @author masensio
|
||||||
|
*/
|
||||||
|
class DownloadRemoteFileOperation(
|
||||||
|
private val remotePath: String,
|
||||||
|
localFolderPath: String,
|
||||||
|
private val spaceWebDavUrl: String? = null,
|
||||||
|
) : RemoteOperation<Unit>() {
|
||||||
|
|
||||||
|
private val cancellationRequested = AtomicBoolean(false)
|
||||||
|
private val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
|
||||||
|
|
||||||
|
var modificationTimestamp: Long = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
var etag: String = ""
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
// download will be performed to a temporal file, then moved to the final location
|
||||||
|
val tmpFile = File(tmpPath)
|
||||||
|
|
||||||
|
// perform the download
|
||||||
|
return try {
|
||||||
|
tmpFile.parentFile?.mkdirs()
|
||||||
|
downloadFile(client, tmpFile).also { result ->
|
||||||
|
Timber.i("Download of $remotePath to $tmpPath: ${result.logMessage}")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
RemoteOperationResult<Unit>(e).also { result ->
|
||||||
|
Timber.e(e, "Download of $remotePath to $tmpPath: ${result.logMessage}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
private fun downloadFile(client: OwnCloudClient, targetFile: File): RemoteOperationResult<Unit> {
|
||||||
|
val result: RemoteOperationResult<Unit>
|
||||||
|
var it: Iterator<OnDatatransferProgressListener>
|
||||||
|
var fos: FileOutputStream? = null
|
||||||
|
var bis: BufferedInputStream? = null
|
||||||
|
var savedFile = false
|
||||||
|
|
||||||
|
val webDavUri = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
|
||||||
|
val getMethod = GetMethod(URL(webDavUri + WebdavUtils.encodePath(remotePath)))
|
||||||
|
|
||||||
|
try {
|
||||||
|
val status = client.executeHttpMethod(getMethod)
|
||||||
|
|
||||||
|
if (isSuccess(status)) {
|
||||||
|
targetFile.createNewFile()
|
||||||
|
bis = BufferedInputStream(getMethod.getResponseBodyAsStream())
|
||||||
|
fos = FileOutputStream(targetFile)
|
||||||
|
var transferred: Long = 0
|
||||||
|
val contentLength = getMethod.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER)
|
||||||
|
val totalToTransfer = if (!contentLength.isNullOrEmpty()) {
|
||||||
|
contentLength.toLong()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
val bytes = ByteArray(4096)
|
||||||
|
var readResult: Int
|
||||||
|
while (bis.read(bytes).also { readResult = it } != -1) {
|
||||||
|
synchronized(cancellationRequested) {
|
||||||
|
if (cancellationRequested.get()) {
|
||||||
|
getMethod.abort()
|
||||||
|
throw OperationCancelledException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fos.write(bytes, 0, readResult)
|
||||||
|
transferred += readResult.toLong()
|
||||||
|
synchronized(dataTransferListeners) {
|
||||||
|
it = dataTransferListeners.iterator()
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next()
|
||||||
|
.onTransferProgress(readResult.toLong(), transferred, totalToTransfer, targetFile.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transferred == totalToTransfer) { // Check if the file is completed
|
||||||
|
savedFile = true
|
||||||
|
val modificationTime =
|
||||||
|
getMethod.getResponseHeaders()?.get("Last-Modified")
|
||||||
|
?: getMethod.getResponseHeader("last-modified")
|
||||||
|
|
||||||
|
if (modificationTime != null) {
|
||||||
|
val modificationDate = WebdavUtils.parseResponseDate(modificationTime)
|
||||||
|
modificationTimestamp = modificationDate?.time ?: 0
|
||||||
|
} else {
|
||||||
|
Timber.e("Could not read modification time from response downloading %s", remotePath)
|
||||||
|
}
|
||||||
|
etag = WebdavUtils.getEtagFromResponse(getMethod)
|
||||||
|
|
||||||
|
// Get rid of extra quotas
|
||||||
|
etag = etag.replace("\"", "")
|
||||||
|
if (etag.isEmpty()) {
|
||||||
|
Timber.e("Could not read eTag from response downloading %s", remotePath)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Timber.e("Content-Length not equal to transferred bytes.")
|
||||||
|
Timber.d("totalToTransfer = $totalToTransfer, transferred = $transferred")
|
||||||
|
client.exhaustResponse(getMethod.getResponseBodyAsStream())
|
||||||
|
// TODO some kind of error control!
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (status != HttpConstants.HTTP_FORBIDDEN && status != HttpConstants.HTTP_SERVICE_UNAVAILABLE) {
|
||||||
|
client.exhaustResponse(getMethod.getResponseBodyAsStream())
|
||||||
|
} // else, body read by RemoteOperationResult constructor
|
||||||
|
|
||||||
|
result =
|
||||||
|
if (isSuccess(status)) {
|
||||||
|
RemoteOperationResult(RemoteOperationResult.ResultCode.OK)
|
||||||
|
} else {
|
||||||
|
RemoteOperationResult(getMethod)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
fos?.close()
|
||||||
|
bis?.close()
|
||||||
|
if (!savedFile && targetFile.exists()) {
|
||||||
|
targetFile.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
|
||||||
|
|
||||||
|
private val tmpPath: String = localFolderPath + remotePath
|
||||||
|
|
||||||
|
fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||||
|
synchronized(dataTransferListeners) { dataTransferListeners.add(listener) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener?) {
|
||||||
|
synchronized(dataTransferListeners) { dataTransferListeners.remove(listener) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
cancellationRequested.set(true) // atomic set; there is no need of synchronizing it
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,7 @@ public class FileUtils {
|
|||||||
public static final String FINAL_CHUNKS_FILE = ".file";
|
public static final String FINAL_CHUNKS_FILE = ".file";
|
||||||
public static final String MIME_DIR = "DIR";
|
public static final String MIME_DIR = "DIR";
|
||||||
public static final String MIME_DIR_UNIX = "httpd/unix-directory";
|
public static final String MIME_DIR_UNIX = "httpd/unix-directory";
|
||||||
|
public static final String MODE_READ_ONLY = "r";
|
||||||
|
|
||||||
static String getParentPath(String remotePath) {
|
static String getParentPath(String remotePath) {
|
||||||
String parentPath = new File(remotePath).getParent();
|
String parentPath = new File(remotePath).getParent();
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.resources.files
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operation to get the base url, which might differ in case of a redirect.
|
||||||
|
*
|
||||||
|
* @author Christian Schabesberger
|
||||||
|
*/
|
||||||
|
|
||||||
|
class GetBaseUrlRemoteOperation : RemoteOperation<String?>() {
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<String?> {
|
||||||
|
return try {
|
||||||
|
val stringUrl = client.baseFilesWebDavUri.toString()
|
||||||
|
|
||||||
|
val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropSet).apply {
|
||||||
|
setReadTimeout(TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(propFindMethod)
|
||||||
|
|
||||||
|
if (isSuccess(status)) {
|
||||||
|
RemoteOperationResult<String?>(RemoteOperationResult.ResultCode.OK).apply {
|
||||||
|
data = propFindMethod.getFinalUrl().toString()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RemoteOperationResult<String?>(propFindMethod).apply {
|
||||||
|
data = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Could not get actuall (or redirected) base URL from base url (/).")
|
||||||
|
RemoteOperationResult<String?>(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Maximum time to wait for a response from the server in milliseconds.
|
||||||
|
*/
|
||||||
|
private const val TIMEOUT = 10_000L
|
||||||
|
}
|
||||||
|
}
|
@ -1,146 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files;
|
|
||||||
|
|
||||||
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.webdav.MoveMethod;
|
|
||||||
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 timber.log.Timber;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
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.
|
|
||||||
* <p>
|
|
||||||
* Allows renaming the moving file/folder at the same time.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
public class MoveRemoteFileOperation extends RemoteOperation {
|
|
||||||
|
|
||||||
private static final int MOVE_READ_TIMEOUT = 600000;
|
|
||||||
private static final int MOVE_CONNECTION_TIMEOUT = 5000;
|
|
||||||
|
|
||||||
private String mSrcRemotePath;
|
|
||||||
private String mTargetRemotePath;
|
|
||||||
private boolean mOverwrite;
|
|
||||||
|
|
||||||
protected boolean moveChunkedFile = false;
|
|
||||||
protected String mFileLastModifTimestamp;
|
|
||||||
protected long mFileLength;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
* <p>
|
|
||||||
* TODO Paths should finish in "/" in the case of folders. ?
|
|
||||||
*
|
|
||||||
* @param srcRemotePath Remote path of the file/folder to move.
|
|
||||||
* @param targetRemotePath Remote path desired for the file/folder after moving it.
|
|
||||||
*/
|
|
||||||
public MoveRemoteFileOperation(String srcRemotePath,
|
|
||||||
String targetRemotePath,
|
|
||||||
boolean overwrite) {
|
|
||||||
|
|
||||||
mSrcRemotePath = srcRemotePath;
|
|
||||||
mTargetRemotePath = targetRemotePath;
|
|
||||||
mOverwrite = overwrite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the rename operation.
|
|
||||||
*
|
|
||||||
* @param client Client object to communicate with the remote ownCloud server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult run(OwnCloudClient client) {
|
|
||||||
if (mTargetRemotePath.equals(mSrcRemotePath)) {
|
|
||||||
// nothing to do!
|
|
||||||
return new RemoteOperationResult<>(ResultCode.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mTargetRemotePath.startsWith(mSrcRemotePath)) {
|
|
||||||
return new RemoteOperationResult<>(ResultCode.INVALID_MOVE_INTO_DESCENDANT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// perform remote operation
|
|
||||||
RemoteOperationResult result;
|
|
||||||
try {
|
|
||||||
// After finishing a chunked upload, we have to move the resulting file from uploads folder to files one,
|
|
||||||
// so this uri has to be customizable
|
|
||||||
Uri srcWebDavUri = moveChunkedFile ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri();
|
|
||||||
|
|
||||||
final MoveMethod move = new MoveMethod(
|
|
||||||
new URL(srcWebDavUri + WebdavUtils.encodePath(mSrcRemotePath)),
|
|
||||||
client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath),
|
|
||||||
mOverwrite);
|
|
||||||
|
|
||||||
if (moveChunkedFile) {
|
|
||||||
move.addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, mFileLastModifTimestamp);
|
|
||||||
move.addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, String.valueOf(mFileLength));
|
|
||||||
}
|
|
||||||
|
|
||||||
move.setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
move.setConnectionTimeout(MOVE_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
final int status = client.executeHttpMethod(move);
|
|
||||||
/// process response
|
|
||||||
if (isSuccess(status)) {
|
|
||||||
result = new RemoteOperationResult<>(ResultCode.OK);
|
|
||||||
} else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !mOverwrite) {
|
|
||||||
|
|
||||||
result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
|
|
||||||
client.exhaustResponse(move.getResponseBodyAsStream());
|
|
||||||
|
|
||||||
/// for other errors that could be explicitly handled, check first:
|
|
||||||
/// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
|
|
||||||
|
|
||||||
} else {
|
|
||||||
result = new RemoteOperationResult<>(move);
|
|
||||||
client.exhaustResponse(move.getResponseBodyAsStream());
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.i("Move " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
result = new RemoteOperationResult<>(e);
|
|
||||||
Timber.e(e, "Move " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isSuccess(int status) {
|
|
||||||
return status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,149 @@
|
|||||||
|
/* 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.files
|
||||||
|
|
||||||
|
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.webdav.MoveMethod
|
||||||
|
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.common.utils.isOneOf
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
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 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>() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the rename operation.
|
||||||
|
*
|
||||||
|
* @param client Client object to communicate with the remote ownCloud server.
|
||||||
|
*/
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
if (targetRemotePath == sourceRemotePath) {
|
||||||
|
// nothing to do!
|
||||||
|
return RemoteOperationResult(ResultCode.OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetRemotePath.startsWith(sourceRemotePath)) {
|
||||||
|
return RemoteOperationResult(ResultCode.INVALID_MOVE_INTO_DESCENDANT)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// perform remote operation
|
||||||
|
var result: RemoteOperationResult<Unit>
|
||||||
|
try {
|
||||||
|
// After finishing a chunked upload, we have to move the resulting file from uploads folder to files one,
|
||||||
|
// so this uri has to be customizable
|
||||||
|
val srcWebDavUri = getSrcWebDavUriForClient(client)
|
||||||
|
val moveMethod = MoveMethod(
|
||||||
|
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)
|
||||||
|
setConnectionTimeout(MOVE_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(moveMethod)
|
||||||
|
|
||||||
|
when {
|
||||||
|
isSuccess(status) -> {
|
||||||
|
result = RemoteOperationResult<Unit>(ResultCode.OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
isPreconditionFailed(status) -> {
|
||||||
|
result = RemoteOperationResult<Unit>(ResultCode.INVALID_OVERWRITE)
|
||||||
|
client.exhaustResponse(moveMethod.getResponseBodyAsStream())
|
||||||
|
|
||||||
|
/// 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.i("Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
result = RemoteOperationResult<Unit>(e)
|
||||||
|
Timber.e(e, "Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
|
||||||
|
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For standard moves, we will use [OwnCloudClient.getUserFilesWebDavUri].
|
||||||
|
* In case we need a different source Uri, override this method.
|
||||||
|
*/
|
||||||
|
open fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.userFilesWebDavUri
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For standard moves, we won't need any special headers.
|
||||||
|
* 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)
|
||||||
|
|
||||||
|
private fun isPreconditionFailed(status: Int) = status == HttpConstants.HTTP_PRECONDITION_FAILED
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
@ -1,107 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2016 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package com.owncloud.android.lib.resources.files;
|
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
|
||||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants;
|
|
||||||
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils;
|
|
||||||
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod;
|
|
||||||
import com.owncloud.android.lib.common.network.WebdavUtils;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperation;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static com.owncloud.android.lib.common.http.methods.webdav.DavConstants.DEPTH_0;
|
|
||||||
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote operation performing the read a file from the ownCloud server.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author masensio
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class ReadRemoteFileOperation extends RemoteOperation<RemoteFile> {
|
|
||||||
|
|
||||||
private static final int SYNC_READ_TIMEOUT = 40000;
|
|
||||||
private static final int SYNC_CONNECTION_TIMEOUT = 5000;
|
|
||||||
|
|
||||||
private String mRemotePath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param remotePath Remote path of the file.
|
|
||||||
*/
|
|
||||||
public ReadRemoteFileOperation(String remotePath) {
|
|
||||||
mRemotePath = remotePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the read operation.
|
|
||||||
*
|
|
||||||
* @param client Client object to communicate with the remote ownCloud server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult<RemoteFile> run(OwnCloudClient client) {
|
|
||||||
PropfindMethod propfind;
|
|
||||||
RemoteOperationResult<RemoteFile> result;
|
|
||||||
|
|
||||||
/// take the duty of check the server for the current state of the file there
|
|
||||||
try {
|
|
||||||
// remote request
|
|
||||||
propfind = new PropfindMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
|
|
||||||
DEPTH_0,
|
|
||||||
DavUtils.getAllPropset());
|
|
||||||
|
|
||||||
propfind.setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
propfind.setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
final int status = client.executeHttpMethod(propfind);
|
|
||||||
|
|
||||||
if (status == HttpConstants.HTTP_MULTI_STATUS
|
|
||||||
|| status == HttpConstants.HTTP_OK) {
|
|
||||||
|
|
||||||
final RemoteFile file = new RemoteFile(propfind.getRoot(), AccountUtils.getUserId(mAccount, mContext));
|
|
||||||
|
|
||||||
result = new RemoteOperationResult<>(OK);
|
|
||||||
result.setData(file);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
result = new RemoteOperationResult<>(propfind);
|
|
||||||
client.exhaustResponse(propfind.getResponseBodyAsStream());
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
result = new RemoteOperationResult<>(e);
|
|
||||||
Timber.e(e, "Synchronizing file %s", mRemotePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,107 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2016 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.resources.files
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.accounts.AccountUtils
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_MULTI_STATUS
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.DavConstants.DEPTH_0
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
|
||||||
|
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.common.utils.isOneOf
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote operation performing the read a file from the ownCloud server.
|
||||||
|
*
|
||||||
|
* @author David A. Velasco
|
||||||
|
* @author masensio
|
||||||
|
* @author David González Verdugo
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ReadRemoteFileOperation(
|
||||||
|
val remotePath: String,
|
||||||
|
val spaceWebDavUrl: String? = null,
|
||||||
|
) : RemoteOperation<RemoteFile>() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the read operation.
|
||||||
|
*
|
||||||
|
* @param client Client object to communicate with the remote ownCloud server.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteFile> {
|
||||||
|
try {
|
||||||
|
val propFind = PropfindMethod(
|
||||||
|
url = getFinalWebDavUrl(),
|
||||||
|
depth = DEPTH_0,
|
||||||
|
propertiesToRequest = DavUtils.allPropSet
|
||||||
|
).apply {
|
||||||
|
setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(propFind)
|
||||||
|
Timber.i("Read remote file $remotePath with status ${propFind.statusCode}")
|
||||||
|
|
||||||
|
return if (isSuccess(status)) {
|
||||||
|
val remoteFile = RemoteFile.getRemoteFileFromDav(
|
||||||
|
davResource = propFind.root!!,
|
||||||
|
userId = AccountUtils.getUserId(mAccount, mContext),
|
||||||
|
userName = mAccount.name,
|
||||||
|
spaceWebDavUrl = spaceWebDavUrl,
|
||||||
|
)
|
||||||
|
|
||||||
|
RemoteOperationResult<RemoteFile>(RemoteOperationResult.ResultCode.OK).apply {
|
||||||
|
data = remoteFile
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RemoteOperationResult<RemoteFile>(propFind).also {
|
||||||
|
client.exhaustResponse(propFind.getResponseBodyAsStream())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
return RemoteOperationResult(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
private const val SYNC_READ_TIMEOUT = 40_000L
|
||||||
|
private const val SYNC_CONNECTION_TIMEOUT = 5_000L
|
||||||
|
}
|
||||||
|
}
|
@ -1,127 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files;
|
|
||||||
|
|
||||||
import at.bitfire.dav4jvm.Response;
|
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
|
||||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants;
|
|
||||||
import com.owncloud.android.lib.common.http.methods.webdav.DavConstants;
|
|
||||||
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils;
|
|
||||||
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod;
|
|
||||||
import com.owncloud.android.lib.common.network.WebdavUtils;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperation;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote operation performing the read of remote file or folder in the ownCloud server.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author masensio
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class ReadRemoteFolderOperation extends RemoteOperation<ArrayList<RemoteFile>> {
|
|
||||||
|
|
||||||
private String mRemotePath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param remotePath Remote path of the file.
|
|
||||||
*/
|
|
||||||
public ReadRemoteFolderOperation(String remotePath) {
|
|
||||||
mRemotePath = remotePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the read operation.
|
|
||||||
*
|
|
||||||
* @param client Client object to communicate with the remote ownCloud server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult<ArrayList<RemoteFile>> run(OwnCloudClient client) {
|
|
||||||
RemoteOperationResult<ArrayList<RemoteFile>> result = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
PropfindMethod propfindMethod = new PropfindMethod(
|
|
||||||
new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
|
|
||||||
DavConstants.DEPTH_1,
|
|
||||||
DavUtils.getAllPropset());
|
|
||||||
|
|
||||||
client.setFollowRedirects(true);
|
|
||||||
|
|
||||||
int status = client.executeHttpMethod(propfindMethod);
|
|
||||||
|
|
||||||
if (isSuccess(status)) {
|
|
||||||
ArrayList<RemoteFile> mFolderAndFiles = new ArrayList<>();
|
|
||||||
|
|
||||||
// parse data from remote folder
|
|
||||||
mFolderAndFiles.add(
|
|
||||||
new RemoteFile(propfindMethod.getRoot(), AccountUtils.getUserId(mAccount, mContext))
|
|
||||||
);
|
|
||||||
|
|
||||||
// loop to update every child
|
|
||||||
for (Response resource : propfindMethod.getMembers()) {
|
|
||||||
RemoteFile file = new RemoteFile(resource, AccountUtils.getUserId(mAccount, mContext));
|
|
||||||
mFolderAndFiles.add(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result of the operation
|
|
||||||
result = new RemoteOperationResult<>(OK);
|
|
||||||
result.setData(mFolderAndFiles);
|
|
||||||
|
|
||||||
} else { // synchronization failed
|
|
||||||
result = new RemoteOperationResult<>(propfindMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
result = new RemoteOperationResult<>(e);
|
|
||||||
} finally {
|
|
||||||
if (result == null) {
|
|
||||||
Timber.e("Synchronized " + mRemotePath + ": result is null");
|
|
||||||
} else if (result.isSuccess()) {
|
|
||||||
Timber.i("Synchronized " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
} else {
|
|
||||||
if (result.isException()) {
|
|
||||||
Timber.e(result.getException(), "Synchronized " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
} else {
|
|
||||||
Timber.e("Synchronized " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSuccess(int status) {
|
|
||||||
return status == HttpConstants.HTTP_MULTI_STATUS || status == HttpConstants.HTTP_OK;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,118 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2020 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.resources.files
|
||||||
|
|
||||||
|
import at.bitfire.dav4jvm.PropertyRegistry
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.accounts.AccountUtils
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_MULTI_STATUS
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.DavConstants
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
|
||||||
|
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||||
|
import com.owncloud.android.lib.common.utils.isOneOf
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote operation performing the read of remote file or folder in the ownCloud server.
|
||||||
|
*
|
||||||
|
* @author David A. Velasco
|
||||||
|
* @author masensio
|
||||||
|
* @author David González Verdugo
|
||||||
|
*/
|
||||||
|
class ReadRemoteFolderOperation(
|
||||||
|
val remotePath: String,
|
||||||
|
val spaceWebDavUrl: String? = null,
|
||||||
|
) : RemoteOperation<ArrayList<RemoteFile>>() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the read operation.
|
||||||
|
*
|
||||||
|
* @param client Client object to communicate with the remote ownCloud server.
|
||||||
|
*/
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<ArrayList<RemoteFile>> {
|
||||||
|
try {
|
||||||
|
PropertyRegistry.register(OCShareTypes.Factory())
|
||||||
|
|
||||||
|
val propfindMethod = PropfindMethod(
|
||||||
|
getFinalWebDavUrl(),
|
||||||
|
DavConstants.DEPTH_1,
|
||||||
|
DavUtils.allPropSet
|
||||||
|
)
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(propfindMethod)
|
||||||
|
|
||||||
|
if (isSuccess(status)) {
|
||||||
|
val mFolderAndFiles = ArrayList<RemoteFile>()
|
||||||
|
|
||||||
|
val remoteFolder = RemoteFile.getRemoteFileFromDav(
|
||||||
|
davResource = propfindMethod.root!!,
|
||||||
|
userId = AccountUtils.getUserId(mAccount, mContext),
|
||||||
|
userName = mAccount.name,
|
||||||
|
spaceWebDavUrl = spaceWebDavUrl,
|
||||||
|
)
|
||||||
|
mFolderAndFiles.add(remoteFolder)
|
||||||
|
|
||||||
|
// loop to update every child
|
||||||
|
propfindMethod.members.forEach { resource ->
|
||||||
|
val remoteFile = RemoteFile.getRemoteFileFromDav(
|
||||||
|
davResource = resource,
|
||||||
|
userId = AccountUtils.getUserId(mAccount, mContext),
|
||||||
|
userName = mAccount.name,
|
||||||
|
spaceWebDavUrl = spaceWebDavUrl,
|
||||||
|
)
|
||||||
|
mFolderAndFiles.add(remoteFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result of the operation
|
||||||
|
return RemoteOperationResult<ArrayList<RemoteFile>>(ResultCode.OK).apply {
|
||||||
|
data = mFolderAndFiles
|
||||||
|
Timber.i("Synchronized $remotePath with ${mFolderAndFiles.size} files. ${this.logMessage}")
|
||||||
|
}
|
||||||
|
} else { // synchronization failed
|
||||||
|
return RemoteOperationResult<ArrayList<RemoteFile>>(propfindMethod).also {
|
||||||
|
Timber.w("Synchronized $remotePath ${it.logMessage}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return RemoteOperationResult<ArrayList<RemoteFile>>(e).also {
|
||||||
|
Timber.e(it.exception, "Synchronized $remotePath")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
@ -1,322 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files;
|
|
||||||
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
|
|
||||||
import at.bitfire.dav4jvm.Property;
|
|
||||||
import at.bitfire.dav4jvm.Response;
|
|
||||||
import at.bitfire.dav4jvm.property.CreationDate;
|
|
||||||
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.QuotaAvailableBytes;
|
|
||||||
import at.bitfire.dav4jvm.property.QuotaUsedBytes;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains the data of a Remote File from a WebDavEntry
|
|
||||||
*
|
|
||||||
* @author masensio
|
|
||||||
* @author Christian Schabesberger
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class RemoteFile implements Parcelable, Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parcelable Methods
|
|
||||||
*/
|
|
||||||
public static final Parcelable.Creator<RemoteFile> CREATOR = new Parcelable.Creator<RemoteFile>() {
|
|
||||||
@Override
|
|
||||||
public RemoteFile createFromParcel(Parcel source) {
|
|
||||||
return new RemoteFile(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RemoteFile[] newArray(int size) {
|
|
||||||
return new RemoteFile[size];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Generated - should be refreshed every time the class changes!!
|
|
||||||
*/
|
|
||||||
private static final long serialVersionUID = -8965995357413958539L;
|
|
||||||
private String mRemotePath;
|
|
||||||
private String mMimeType;
|
|
||||||
private long mLength;
|
|
||||||
private long mCreationTimestamp;
|
|
||||||
private long mModifiedTimestamp;
|
|
||||||
private String mEtag;
|
|
||||||
private String mPermissions;
|
|
||||||
private String mRemoteId;
|
|
||||||
private long mSize;
|
|
||||||
private BigDecimal mQuotaUsedBytes;
|
|
||||||
private BigDecimal mQuotaAvailableBytes;
|
|
||||||
private String mPrivateLink;
|
|
||||||
|
|
||||||
public RemoteFile() {
|
|
||||||
resetData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create new {@link RemoteFile} with given path.
|
|
||||||
* <p>
|
|
||||||
* The path received must be URL-decoded. Path separator must be File.separator, and it must be the first
|
|
||||||
* character in 'path'.
|
|
||||||
*
|
|
||||||
* @param path The remote path of the file.
|
|
||||||
*/
|
|
||||||
public RemoteFile(String path) {
|
|
||||||
resetData();
|
|
||||||
if (path == null || path.length() <= 0 || !path.startsWith(File.separator)) {
|
|
||||||
throw new IllegalArgumentException("Trying to create a OCFile with a non valid remote path: " + path);
|
|
||||||
}
|
|
||||||
mRemotePath = path;
|
|
||||||
mCreationTimestamp = 0;
|
|
||||||
mLength = 0;
|
|
||||||
mMimeType = FileUtils.MIME_DIR;
|
|
||||||
mQuotaUsedBytes = BigDecimal.ZERO;
|
|
||||||
mQuotaAvailableBytes = BigDecimal.ZERO;
|
|
||||||
mPrivateLink = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RemoteFile(final Response davResource, String userId) {
|
|
||||||
this(RemoteFileUtil.Companion.getRemotePathFromUrl(davResource.getHref(), userId));
|
|
||||||
final List<Property> properties = davResource.getProperties();
|
|
||||||
|
|
||||||
for (Property property : properties) {
|
|
||||||
if (property instanceof CreationDate) {
|
|
||||||
this.setCreationTimestamp(
|
|
||||||
Long.parseLong(((CreationDate) property).getCreationDate()));
|
|
||||||
}
|
|
||||||
if (property instanceof GetContentLength) {
|
|
||||||
this.setLength(((GetContentLength) property).getContentLength());
|
|
||||||
}
|
|
||||||
if (property instanceof GetContentType) {
|
|
||||||
this.setMimeType(((GetContentType) property).getType());
|
|
||||||
}
|
|
||||||
if (property instanceof GetLastModified) {
|
|
||||||
this.setModifiedTimestamp(((GetLastModified) property).getLastModified());
|
|
||||||
}
|
|
||||||
if (property instanceof GetETag) {
|
|
||||||
this.setEtag(((GetETag) property).getETag());
|
|
||||||
}
|
|
||||||
if (property instanceof OCPermissions) {
|
|
||||||
this.setPermissions(((OCPermissions) property).getPermission());
|
|
||||||
}
|
|
||||||
if (property instanceof OCId) {
|
|
||||||
this.setRemoteId(((OCId) property).getId());
|
|
||||||
}
|
|
||||||
if (property instanceof OCSize) {
|
|
||||||
this.setSize(((OCSize) property).getSize());
|
|
||||||
}
|
|
||||||
if (property instanceof QuotaUsedBytes) {
|
|
||||||
this.setQuotaUsedBytes(
|
|
||||||
BigDecimal.valueOf(((QuotaUsedBytes) property).getQuotaUsedBytes()));
|
|
||||||
}
|
|
||||||
if (property instanceof QuotaAvailableBytes) {
|
|
||||||
this.setQuotaAvailableBytes(
|
|
||||||
BigDecimal.valueOf(((QuotaAvailableBytes) property).getQuotaAvailableBytes()));
|
|
||||||
}
|
|
||||||
if (property instanceof OCPrivatelink) {
|
|
||||||
this.setPrivateLink(((OCPrivatelink) property).getLink());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconstruct from parcel
|
|
||||||
*
|
|
||||||
* @param source The source parcel
|
|
||||||
*/
|
|
||||||
protected RemoteFile(Parcel source) {
|
|
||||||
readFromParcel(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this to find out if this file is a folder.
|
|
||||||
*
|
|
||||||
* @return true if it is a folder
|
|
||||||
*/
|
|
||||||
public boolean isFolder() {
|
|
||||||
return mMimeType != null && (mMimeType.equals(FileUtils.MIME_DIR) || mMimeType.equals(FileUtils.MIME_DIR_UNIX));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Getters and Setters
|
|
||||||
*/
|
|
||||||
|
|
||||||
public String getRemotePath() {
|
|
||||||
return mRemotePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRemotePath(String remotePath) {
|
|
||||||
this.mRemotePath = remotePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMimeType() {
|
|
||||||
return mMimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMimeType(String mimeType) {
|
|
||||||
this.mMimeType = mimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLength() {
|
|
||||||
return mLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLength(long length) {
|
|
||||||
this.mLength = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCreationTimestamp() {
|
|
||||||
return mCreationTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCreationTimestamp(long creationTimestamp) {
|
|
||||||
this.mCreationTimestamp = creationTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getModifiedTimestamp() {
|
|
||||||
return mModifiedTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setModifiedTimestamp(long modifiedTimestamp) {
|
|
||||||
this.mModifiedTimestamp = modifiedTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEtag() {
|
|
||||||
return mEtag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEtag(String etag) {
|
|
||||||
this.mEtag = etag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPermissions() {
|
|
||||||
return mPermissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPermissions(String permissions) {
|
|
||||||
this.mPermissions = permissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRemoteId() {
|
|
||||||
return mRemoteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRemoteId(String remoteId) {
|
|
||||||
this.mRemoteId = remoteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSize() {
|
|
||||||
return mSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSize(long size) {
|
|
||||||
mSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQuotaUsedBytes(BigDecimal quotaUsedBytes) {
|
|
||||||
mQuotaUsedBytes = quotaUsedBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQuotaAvailableBytes(BigDecimal quotaAvailableBytes) {
|
|
||||||
mQuotaAvailableBytes = quotaAvailableBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPrivateLink() {
|
|
||||||
return mPrivateLink;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrivateLink(String privateLink) {
|
|
||||||
mPrivateLink = privateLink;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used internally. Reset all file properties
|
|
||||||
*/
|
|
||||||
private void resetData() {
|
|
||||||
mRemotePath = null;
|
|
||||||
mMimeType = null;
|
|
||||||
mLength = 0;
|
|
||||||
mCreationTimestamp = 0;
|
|
||||||
mModifiedTimestamp = 0;
|
|
||||||
mEtag = null;
|
|
||||||
mPermissions = null;
|
|
||||||
mRemoteId = null;
|
|
||||||
mSize = 0;
|
|
||||||
mQuotaUsedBytes = null;
|
|
||||||
mQuotaAvailableBytes = null;
|
|
||||||
mPrivateLink = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readFromParcel(Parcel source) {
|
|
||||||
mRemotePath = source.readString();
|
|
||||||
mMimeType = source.readString();
|
|
||||||
mLength = source.readLong();
|
|
||||||
mCreationTimestamp = source.readLong();
|
|
||||||
mModifiedTimestamp = source.readLong();
|
|
||||||
mEtag = source.readString();
|
|
||||||
mPermissions = source.readString();
|
|
||||||
mRemoteId = source.readString();
|
|
||||||
mSize = source.readLong();
|
|
||||||
mQuotaUsedBytes = (BigDecimal) source.readSerializable();
|
|
||||||
mQuotaAvailableBytes = (BigDecimal) source.readSerializable();
|
|
||||||
mPrivateLink = source.readString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return this.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
|
||||||
dest.writeString(mRemotePath);
|
|
||||||
dest.writeString(mMimeType);
|
|
||||||
dest.writeLong(mLength);
|
|
||||||
dest.writeLong(mCreationTimestamp);
|
|
||||||
dest.writeLong(mModifiedTimestamp);
|
|
||||||
dest.writeString(mEtag);
|
|
||||||
dest.writeString(mPermissions);
|
|
||||||
dest.writeString(mRemoteId);
|
|
||||||
dest.writeLong(mSize);
|
|
||||||
dest.writeSerializable(mQuotaUsedBytes);
|
|
||||||
dest.writeSerializable(mQuotaAvailableBytes);
|
|
||||||
dest.writeString(mPrivateLink);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,194 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2020 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.lib.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
|
||||||
|
import at.bitfire.dav4jvm.property.CreationDate
|
||||||
|
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 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
|
||||||
|
import com.owncloud.android.lib.common.utils.isOneOf
|
||||||
|
import com.owncloud.android.lib.resources.shares.ShareType
|
||||||
|
import com.owncloud.android.lib.resources.shares.ShareType.Companion.fromValue
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the data of a Remote File from a WebDavEntry
|
||||||
|
*
|
||||||
|
* The path received must be URL-decoded. Path separator must be File.separator, and it must be the first character in 'path'.
|
||||||
|
*
|
||||||
|
* @author masensio
|
||||||
|
* @author Christian Schabesberger
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data class RemoteFile(
|
||||||
|
var remotePath: String,
|
||||||
|
var mimeType: String = "DIR",
|
||||||
|
var length: Long = 0,
|
||||||
|
var creationTimestamp: Long = 0,
|
||||||
|
var modifiedTimestamp: Long = 0,
|
||||||
|
var etag: String? = null,
|
||||||
|
var permissions: String? = null,
|
||||||
|
var remoteId: String? = null,
|
||||||
|
var size: Long = 0,
|
||||||
|
var privateLink: String? = null,
|
||||||
|
var owner: String,
|
||||||
|
var sharedByLink: Boolean = false,
|
||||||
|
var sharedWithSharee: Boolean = false,
|
||||||
|
) : Parcelable {
|
||||||
|
|
||||||
|
// TODO: Quotas not used. Use or remove them.
|
||||||
|
init {
|
||||||
|
require(
|
||||||
|
!(remotePath.isEmpty() || !remotePath.startsWith(File.separator))
|
||||||
|
) { "Trying to create a OCFile with a non valid remote path: $remotePath" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to find out if this file is a folder.
|
||||||
|
*
|
||||||
|
* @return true if it is a folder
|
||||||
|
*/
|
||||||
|
val isFolder
|
||||||
|
get() = mimeType.isOneOf(MIME_DIR, MIME_DIR_UNIX)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val MIME_DIR = "DIR"
|
||||||
|
const val MIME_DIR_UNIX = "httpd/unix-directory"
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
for (property in properties) {
|
||||||
|
when (property) {
|
||||||
|
is CreationDate -> {
|
||||||
|
remoteFile.creationTimestamp = property.creationDate.toLong()
|
||||||
|
}
|
||||||
|
is GetContentLength -> {
|
||||||
|
remoteFile.length = property.contentLength
|
||||||
|
}
|
||||||
|
is GetContentType -> {
|
||||||
|
property.type?.let { remoteFile.mimeType = it }
|
||||||
|
}
|
||||||
|
is GetLastModified -> {
|
||||||
|
remoteFile.modifiedTimestamp = property.lastModified
|
||||||
|
}
|
||||||
|
is GetETag -> {
|
||||||
|
remoteFile.etag = property.eTag
|
||||||
|
}
|
||||||
|
is OCPermissions -> {
|
||||||
|
remoteFile.permissions = property.permission
|
||||||
|
}
|
||||||
|
is OCId -> {
|
||||||
|
remoteFile.remoteId = property.id
|
||||||
|
}
|
||||||
|
is OCSize -> {
|
||||||
|
remoteFile.size = property.size
|
||||||
|
}
|
||||||
|
is OCPrivatelink -> {
|
||||||
|
remoteFile.privateLink = property.link
|
||||||
|
}
|
||||||
|
is OCShareTypes -> {
|
||||||
|
val list = property.shareTypes
|
||||||
|
for (i in list.indices) {
|
||||||
|
val shareType = fromValue(list[i].toInt())
|
||||||
|
if (shareType == null) {
|
||||||
|
Timber.d("Illegal share type value: " + list[i])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (shareType == ShareType.PUBLIC_LINK) {
|
||||||
|
remoteFile.sharedByLink = true
|
||||||
|
} else if (shareType == ShareType.USER || shareType == ShareType.FEDERATED || shareType == ShareType.GROUP) {
|
||||||
|
remoteFile.sharedWithSharee = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remoteFile
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a relative path from a remote file url
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
@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, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPropertiesEvenIfPostProcessing(response: Response): List<Property> {
|
||||||
|
return if (response.isSuccess())
|
||||||
|
response.propstat.filter { propStat -> propStat.isSuccessOrPostProcessing() }.map { it.properties }.flatten()
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PropStat.isSuccessOrPostProcessing() = (status.code / 100 == 2 || status.code == HttpConstants.HTTP_TOO_EARLY)
|
||||||
|
}
|
||||||
|
}
|
@ -1,96 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files;
|
|
||||||
|
|
||||||
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.DeleteMethod;
|
|
||||||
import com.owncloud.android.lib.common.network.WebdavUtils;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperation;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote operation performing the removal of a remote file or folder in the ownCloud server.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author masensio
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
public class RemoveRemoteFileOperation extends RemoteOperation {
|
|
||||||
private String mRemotePath;
|
|
||||||
|
|
||||||
protected boolean removeChunksFolder = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param remotePath RemotePath of the remote file or folder to remove from the server
|
|
||||||
*/
|
|
||||||
public RemoveRemoteFileOperation(String remotePath) {
|
|
||||||
mRemotePath = remotePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the rename operation.
|
|
||||||
*
|
|
||||||
* @param client Client object to communicate with the remote ownCloud server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult run(OwnCloudClient client) {
|
|
||||||
RemoteOperationResult result;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Uri srcWebDavUri = removeChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri();
|
|
||||||
|
|
||||||
DeleteMethod deleteMethod = new DeleteMethod(
|
|
||||||
new URL(srcWebDavUri + WebdavUtils.encodePath(mRemotePath)));
|
|
||||||
|
|
||||||
int status = client.executeHttpMethod(deleteMethod);
|
|
||||||
|
|
||||||
result = isSuccess(status) ?
|
|
||||||
new RemoteOperationResult<>(OK) :
|
|
||||||
new RemoteOperationResult<>(deleteMethod);
|
|
||||||
|
|
||||||
Timber.i("Remove " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
result = new RemoteOperationResult<>(e);
|
|
||||||
Timber.e(e, "Remove " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSuccess(int status) {
|
|
||||||
return status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_NO_CONTENT;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,81 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2020 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.lib.resources.files
|
||||||
|
|
||||||
|
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
|
||||||
|
import com.owncloud.android.lib.common.http.methods.nonwebdav.DeleteMethod
|
||||||
|
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.common.utils.isOneOf
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote operation performing the removal of a remote file or folder in the ownCloud server.
|
||||||
|
*
|
||||||
|
* @author David A. Velasco
|
||||||
|
* @author masensio
|
||||||
|
* @author David González Verdugo
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*/
|
||||||
|
open class RemoveRemoteFileOperation(
|
||||||
|
private val remotePath: String,
|
||||||
|
val spaceWebDavUrl: String? = null,
|
||||||
|
) : RemoteOperation<Unit>() {
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
var result: RemoteOperationResult<Unit>
|
||||||
|
try {
|
||||||
|
val srcWebDavUri = getSrcWebDavUriForClient(client)
|
||||||
|
val deleteMethod = DeleteMethod(
|
||||||
|
URL(srcWebDavUri + WebdavUtils.encodePath(remotePath))
|
||||||
|
)
|
||||||
|
val status = client.executeHttpMethod(deleteMethod)
|
||||||
|
|
||||||
|
result = if (isSuccess(status)) {
|
||||||
|
RemoteOperationResult<Unit>(ResultCode.OK)
|
||||||
|
} else {
|
||||||
|
RemoteOperationResult<Unit>(deleteMethod)
|
||||||
|
}
|
||||||
|
Timber.i("Remove $remotePath: ${result.logMessage}")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
result = RemoteOperationResult<Unit>(e)
|
||||||
|
Timber.e(e, "Remove $remotePath: ${result.logMessage}")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For standard removals, we will use [OwnCloudClient.getUserFilesWebDavUri].
|
||||||
|
* In case we need a different source Uri, override this method.
|
||||||
|
*/
|
||||||
|
open fun getSrcWebDavUriForClient(client: OwnCloudClient): String = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
|
||||||
|
|
||||||
|
private fun isSuccess(status: Int) = status.isOneOf(HTTP_OK, HTTP_NO_CONTENT)
|
||||||
|
}
|
@ -1,130 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2019 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files;
|
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants;
|
|
||||||
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod;
|
|
||||||
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 timber.log.Timber;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote operation performing the rename of a remote file or folder in the ownCloud server.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author masensio
|
|
||||||
*/
|
|
||||||
public class RenameRemoteFileOperation extends RemoteOperation {
|
|
||||||
|
|
||||||
private static final int RENAME_READ_TIMEOUT = 600000;
|
|
||||||
private static final int RENAME_CONNECTION_TIMEOUT = 5000;
|
|
||||||
|
|
||||||
private String mOldName;
|
|
||||||
private String mOldRemotePath;
|
|
||||||
private String mNewName;
|
|
||||||
private String mNewRemotePath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param oldName Old name of the file.
|
|
||||||
* @param oldRemotePath Old remote path of the file.
|
|
||||||
* @param newName New name to set as the name of file.
|
|
||||||
* @param isFolder 'true' for folder and 'false' for files
|
|
||||||
*/
|
|
||||||
public RenameRemoteFileOperation(String oldName, String oldRemotePath, String newName,
|
|
||||||
boolean isFolder) {
|
|
||||||
mOldName = oldName;
|
|
||||||
mOldRemotePath = oldRemotePath;
|
|
||||||
mNewName = newName;
|
|
||||||
|
|
||||||
String parent = (new File(mOldRemotePath)).getParent();
|
|
||||||
parent = (parent.endsWith(File.separator)) ? parent : parent + File.separator;
|
|
||||||
mNewRemotePath = parent + mNewName;
|
|
||||||
if (isFolder) {
|
|
||||||
mNewRemotePath += File.separator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the rename operation.
|
|
||||||
*
|
|
||||||
* @param client Client object to communicate with the remote ownCloud server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult run(OwnCloudClient client) {
|
|
||||||
try {
|
|
||||||
if (mNewName.equals(mOldName)) {
|
|
||||||
return new RemoteOperationResult<>(ResultCode.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetPathIsUsed(client)) {
|
|
||||||
return new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
final MoveMethod move = new MoveMethod(new URL(client.getUserFilesWebDavUri() +
|
|
||||||
WebdavUtils.encodePath(mOldRemotePath)),
|
|
||||||
client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mNewRemotePath), false);
|
|
||||||
|
|
||||||
move.setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
move.setConnectionTimeout(RENAME_READ_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
final int status = client.executeHttpMethod(move);
|
|
||||||
final RemoteOperationResult result =
|
|
||||||
(status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT)
|
|
||||||
? new RemoteOperationResult<>(ResultCode.OK)
|
|
||||||
: new RemoteOperationResult<>(move);
|
|
||||||
|
|
||||||
Timber.i("Rename " + mOldRemotePath + " to " + mNewRemotePath + ": " + result.getLogMessage());
|
|
||||||
client.exhaustResponse(move.getResponseBodyAsStream());
|
|
||||||
return result;
|
|
||||||
} catch (Exception e) {
|
|
||||||
final RemoteOperationResult result = new RemoteOperationResult<>(e);
|
|
||||||
Timber.e(e,
|
|
||||||
"Rename " + mOldRemotePath + " to " + ((mNewRemotePath == null) ? mNewName : mNewRemotePath) + ":" +
|
|
||||||
" " + result.getLogMessage());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a file with the new name already exists.
|
|
||||||
*
|
|
||||||
* @return 'True' if the target path is already used by an existing file.
|
|
||||||
*/
|
|
||||||
private boolean targetPathIsUsed(OwnCloudClient client) {
|
|
||||||
CheckPathExistenceRemoteOperation checkPathExistenceRemoteOperation =
|
|
||||||
new CheckPathExistenceRemoteOperation(mNewRemotePath, false);
|
|
||||||
RemoteOperationResult exists = checkPathExistenceRemoteOperation.execute(client);
|
|
||||||
return exists.isSuccess();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,120 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2021 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.lib.resources.files
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod
|
||||||
|
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.common.utils.isOneOf
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote operation performing the rename of a remote file or folder in the ownCloud server.
|
||||||
|
*
|
||||||
|
* @author David A. Velasco
|
||||||
|
* @author masensio
|
||||||
|
*/
|
||||||
|
class RenameRemoteFileOperation(
|
||||||
|
private val oldName: String,
|
||||||
|
private val oldRemotePath: String,
|
||||||
|
private val newName: String,
|
||||||
|
isFolder: Boolean,
|
||||||
|
val spaceWebDavUrl: String? = null,
|
||||||
|
) : RemoteOperation<Unit>() {
|
||||||
|
|
||||||
|
private var newRemotePath: String
|
||||||
|
|
||||||
|
init {
|
||||||
|
var parent = (File(oldRemotePath)).parent ?: throw IllegalArgumentException()
|
||||||
|
if (!parent.endsWith(File.separator)) {
|
||||||
|
parent = parent.plus(File.separator)
|
||||||
|
}
|
||||||
|
newRemotePath = parent.plus(newName)
|
||||||
|
if (isFolder) {
|
||||||
|
newRemotePath.plus(File.separator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
var result: RemoteOperationResult<Unit>
|
||||||
|
try {
|
||||||
|
if (newName == oldName) {
|
||||||
|
return RemoteOperationResult<Unit>(ResultCode.OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetPathIsUsed(client)) {
|
||||||
|
return RemoteOperationResult<Unit>(ResultCode.INVALID_OVERWRITE)
|
||||||
|
}
|
||||||
|
|
||||||
|
val moveMethod: MoveMethod = MoveMethod(
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
val status = client.executeHttpMethod(moveMethod)
|
||||||
|
|
||||||
|
result = if (isSuccess(status)) {
|
||||||
|
RemoteOperationResult<Unit>(ResultCode.OK)
|
||||||
|
} else {
|
||||||
|
RemoteOperationResult<Unit>(moveMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.i("Rename $oldRemotePath to $newRemotePath: ${result.logMessage}")
|
||||||
|
client.exhaustResponse(moveMethod.getResponseBodyAsStream())
|
||||||
|
return result
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
result = RemoteOperationResult<Unit>(exception)
|
||||||
|
Timber.e(exception, "Rename $oldRemotePath to $newName: ${result.logMessage}")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a file with the new name already exists.
|
||||||
|
*
|
||||||
|
* @return 'True' if the target path is already used by an existing file.
|
||||||
|
*/
|
||||||
|
private fun targetPathIsUsed(client: OwnCloudClient): Boolean {
|
||||||
|
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation(newRemotePath, true)
|
||||||
|
val exists = checkPathExistenceRemoteOperation.execute(client)
|
||||||
|
return exists.isSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val RENAME_READ_TIMEOUT = 10_000L
|
||||||
|
private const val RENAME_CONNECTION_TIMEOUT = 5_000L
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.lib.resources.files
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod
|
||||||
|
import com.owncloud.android.lib.common.network.ContentUriRequestBody
|
||||||
|
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.utils.isOneOf
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
class UploadFileFromContentUriOperation(
|
||||||
|
private val uploadPath: String,
|
||||||
|
private val lastModified: String,
|
||||||
|
private val requestBody: ContentUriRequestBody
|
||||||
|
) : RemoteOperation<Unit>() {
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
val putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(uploadPath)), requestBody).apply {
|
||||||
|
retryOnConnectionFailure = false
|
||||||
|
addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, requestBody.contentLength().toString())
|
||||||
|
addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, lastModified)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val status = client.executeHttpMethod(putMethod)
|
||||||
|
if (isSuccess(status)) {
|
||||||
|
RemoteOperationResult<Unit>(RemoteOperationResult.ResultCode.OK).apply { data = Unit }
|
||||||
|
} else {
|
||||||
|
RemoteOperationResult<Unit>(putMethod)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val result = RemoteOperationResult<Unit>(e)
|
||||||
|
Timber.e(e, "Upload from content uri failed : ${result.logMessage}")
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSuccess(status: Int): Boolean {
|
||||||
|
return status.isOneOf(HttpConstants.HTTP_OK, HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,148 @@
|
|||||||
|
/* 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
|
||||||
|
* NONINFINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.resources.files
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod
|
||||||
|
import com.owncloud.android.lib.common.network.FileRequestBody
|
||||||
|
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener
|
||||||
|
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||||
|
import com.owncloud.android.lib.common.operations.OperationCancelledException
|
||||||
|
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.common.utils.isOneOf
|
||||||
|
import okhttp3.MediaType
|
||||||
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote operation performing the upload of a remote file to the ownCloud server.
|
||||||
|
*
|
||||||
|
* @author David A. Velasco
|
||||||
|
* @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,
|
||||||
|
val remotePath: String,
|
||||||
|
val mimeType: String,
|
||||||
|
val lastModifiedTimestamp: String,
|
||||||
|
val requiredEtag: String?,
|
||||||
|
val spaceWebDavUrl: String? = null,
|
||||||
|
) : RemoteOperation<Unit>() {
|
||||||
|
|
||||||
|
protected val cancellationRequested = AtomicBoolean(false)
|
||||||
|
protected var putMethod: PutMethod? = null
|
||||||
|
protected val dataTransferListener: MutableSet<OnDatatransferProgressListener> = HashSet()
|
||||||
|
protected var fileRequestBody: FileRequestBody? = null
|
||||||
|
|
||||||
|
var etag: String = ""
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
var result: RemoteOperationResult<Unit>
|
||||||
|
try {
|
||||||
|
if (cancellationRequested.get()) {
|
||||||
|
// the operation was cancelled before getting it's turn to be executed in the queue of uploads
|
||||||
|
result = RemoteOperationResult<Unit>(OperationCancelledException())
|
||||||
|
Timber.i("Upload of $localPath to $remotePath has been cancelled")
|
||||||
|
} else {
|
||||||
|
// perform the upload
|
||||||
|
result = uploadFile(client)
|
||||||
|
Timber.i("Upload of $localPath to $remotePath: ${result.logMessage}")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (putMethod?.isAborted == true) {
|
||||||
|
result = RemoteOperationResult<Unit>(OperationCancelledException())
|
||||||
|
Timber.e(result.exception, "Upload of $localPath to $remotePath has been aborted with this message: ${result.logMessage}")
|
||||||
|
} else {
|
||||||
|
result = RemoteOperationResult<Unit>(e)
|
||||||
|
Timber.e(result.exception, "Upload of $localPath to $remotePath has failed with this message: ${result.logMessage}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
protected open fun uploadFile(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
val fileToUpload = File(localPath)
|
||||||
|
val mediaType: MediaType? = mimeType.toMediaTypeOrNull()
|
||||||
|
|
||||||
|
fileRequestBody = FileRequestBody(fileToUpload, mediaType).also {
|
||||||
|
synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) }
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, fileToUpload.length().toString())
|
||||||
|
addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, lastModifiedTimestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(putMethod)
|
||||||
|
return if (isSuccess(status)) {
|
||||||
|
etag = WebdavUtils.getEtagFromResponse(putMethod)
|
||||||
|
// Get rid of extra quotas
|
||||||
|
etag = etag.replace("\"", "")
|
||||||
|
if (etag.isEmpty()) {
|
||||||
|
Timber.e("Could not read eTag from response uploading %s", localPath)
|
||||||
|
} else {
|
||||||
|
Timber.d("File uploaded successfully. New etag for file ${fileToUpload.name} is $etag")
|
||||||
|
}
|
||||||
|
RemoteOperationResult<Unit>(ResultCode.OK).apply { data = Unit }
|
||||||
|
} else { // synchronization failed
|
||||||
|
RemoteOperationResult<Unit>(putMethod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addDataTransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||||
|
synchronized(dataTransferListener) { dataTransferListener.add(listener) }
|
||||||
|
fileRequestBody?.addDatatransferProgressListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeDataTransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||||
|
synchronized(dataTransferListener) { dataTransferListener.remove(listener) }
|
||||||
|
fileRequestBody?.removeDatatransferProgressListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
synchronized(cancellationRequested) {
|
||||||
|
cancellationRequested.set(true)
|
||||||
|
putMethod?.abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSuccess(status: Int): Boolean {
|
||||||
|
return status.isOneOf(HttpConstants.HTTP_OK, HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
|
||||||
|
}
|
||||||
|
}
|
@ -1,181 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.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.PutMethod;
|
|
||||||
import com.owncloud.android.lib.common.network.FileRequestBody;
|
|
||||||
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
|
|
||||||
import com.owncloud.android.lib.common.network.WebdavUtils;
|
|
||||||
import com.owncloud.android.lib.common.operations.OperationCancelledException;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperation;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
|
||||||
import okhttp3.MediaType;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote operation performing the upload of a remote file to the ownCloud server.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author masensio
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class UploadRemoteFileOperation extends RemoteOperation {
|
|
||||||
|
|
||||||
protected final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
|
|
||||||
protected String mLocalPath;
|
|
||||||
protected String mRemotePath;
|
|
||||||
protected String mMimeType;
|
|
||||||
protected String mFileLastModifTimestamp;
|
|
||||||
protected PutMethod mPutMethod = null;
|
|
||||||
protected String mRequiredEtag = null;
|
|
||||||
protected Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
|
|
||||||
|
|
||||||
protected FileRequestBody mFileRequestBody = null;
|
|
||||||
|
|
||||||
public UploadRemoteFileOperation(String localPath, String remotePath, String mimeType,
|
|
||||||
String fileLastModifTimestamp) {
|
|
||||||
mLocalPath = localPath;
|
|
||||||
mRemotePath = remotePath;
|
|
||||||
mMimeType = mimeType;
|
|
||||||
mFileLastModifTimestamp = fileLastModifTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UploadRemoteFileOperation(String localPath, String remotePath, String mimeType,
|
|
||||||
String requiredEtag, String fileLastModifTimestamp) {
|
|
||||||
this(localPath, remotePath, mimeType, fileLastModifTimestamp);
|
|
||||||
mRequiredEtag = requiredEtag;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult run(OwnCloudClient client) {
|
|
||||||
RemoteOperationResult result;
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
if (mCancellationRequested.get()) {
|
|
||||||
// the operation was cancelled before getting it's turn to be executed in the queue of uploads
|
|
||||||
result = new RemoteOperationResult<>(new OperationCancelledException());
|
|
||||||
} else {
|
|
||||||
// perform the upload
|
|
||||||
result = uploadFile(client);
|
|
||||||
Timber.i("Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
if (mPutMethod != null && mPutMethod.isAborted()) {
|
|
||||||
result = new RemoteOperationResult<>(new OperationCancelledException());
|
|
||||||
Timber.e(result.getException(),
|
|
||||||
"Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
} else {
|
|
||||||
result = new RemoteOperationResult<>(e);
|
|
||||||
Timber.e(e, "Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected RemoteOperationResult<?> uploadFile(OwnCloudClient client) throws Exception {
|
|
||||||
|
|
||||||
File fileToUpload = new File(mLocalPath);
|
|
||||||
|
|
||||||
MediaType mediaType = MediaType.parse(mMimeType);
|
|
||||||
|
|
||||||
mFileRequestBody = new FileRequestBody(fileToUpload, mediaType);
|
|
||||||
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners);
|
|
||||||
}
|
|
||||||
|
|
||||||
mPutMethod = new PutMethod(
|
|
||||||
new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), mFileRequestBody);
|
|
||||||
|
|
||||||
mPutMethod.setRetryOnConnectionFailure(false);
|
|
||||||
|
|
||||||
if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
|
|
||||||
mPutMethod.addRequestHeader(HttpConstants.IF_MATCH_HEADER, mRequiredEtag);
|
|
||||||
}
|
|
||||||
|
|
||||||
mPutMethod.addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, String.valueOf(fileToUpload.length()));
|
|
||||||
mPutMethod.addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, mFileLastModifTimestamp);
|
|
||||||
|
|
||||||
int status = client.executeHttpMethod(mPutMethod);
|
|
||||||
|
|
||||||
if (isSuccess(status)) {
|
|
||||||
return new RemoteOperationResult<>(OK);
|
|
||||||
|
|
||||||
} else { // synchronization failed
|
|
||||||
return new RemoteOperationResult<>(mPutMethod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<OnDatatransferProgressListener> getDataTransferListeners() {
|
|
||||||
return mDataTransferListeners;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
mDataTransferListeners.add(listener);
|
|
||||||
}
|
|
||||||
if (mFileRequestBody != null) {
|
|
||||||
mFileRequestBody.addDatatransferProgressListener(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
mDataTransferListeners.remove(listener);
|
|
||||||
}
|
|
||||||
if (mFileRequestBody != null) {
|
|
||||||
mFileRequestBody.removeDatatransferProgressListener(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancel() {
|
|
||||||
synchronized (mCancellationRequested) {
|
|
||||||
mCancellationRequested.set(true);
|
|
||||||
if (mPutMethod != null) {
|
|
||||||
mPutMethod.abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSuccess(int status) {
|
|
||||||
return ((status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_CREATED ||
|
|
||||||
status == HttpConstants.HTTP_NO_CONTENT));
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,122 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2022 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.lib.resources.files.chunks
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod
|
||||||
|
import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody
|
||||||
|
import com.owncloud.android.lib.common.operations.OperationCancelledException
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||||
|
import com.owncloud.android.lib.resources.files.FileUtils.MODE_READ_ONLY
|
||||||
|
import com.owncloud.android.lib.resources.files.UploadFileFromFileSystemOperation
|
||||||
|
import okhttp3.MediaType
|
||||||
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
import java.io.RandomAccessFile
|
||||||
|
import java.net.URL
|
||||||
|
import java.nio.channels.FileChannel
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote operation performing the chunked upload of a remote file to the ownCloud server.
|
||||||
|
*
|
||||||
|
* @author David A. Velasco
|
||||||
|
* @author David González Verdugo
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*/
|
||||||
|
class ChunkedUploadFromFileSystemOperation(
|
||||||
|
private val transferId: String,
|
||||||
|
localPath: String,
|
||||||
|
remotePath: String,
|
||||||
|
mimeType: String,
|
||||||
|
lastModifiedTimestamp: String,
|
||||||
|
requiredEtag: String?,
|
||||||
|
) : UploadFileFromFileSystemOperation(
|
||||||
|
localPath = localPath,
|
||||||
|
remotePath = remotePath,
|
||||||
|
mimeType = mimeType,
|
||||||
|
lastModifiedTimestamp = lastModifiedTimestamp,
|
||||||
|
requiredEtag = requiredEtag
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun uploadFile(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
lateinit var result: RemoteOperationResult<Unit>
|
||||||
|
|
||||||
|
val fileToUpload = File(localPath)
|
||||||
|
val mediaType: MediaType? = mimeType.toMediaTypeOrNull()
|
||||||
|
val raf = RandomAccessFile(fileToUpload, MODE_READ_ONLY)
|
||||||
|
val channel: FileChannel = raf.channel
|
||||||
|
|
||||||
|
val fileRequestBody = ChunkFromFileRequestBody(fileToUpload, mediaType, channel).also {
|
||||||
|
synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val uriPrefix = client.uploadsWebDavUri.toString() + File.separator + transferId
|
||||||
|
val totalLength = fileToUpload.length()
|
||||||
|
val chunkCount = ceil(totalLength.toDouble() / CHUNK_SIZE).toLong()
|
||||||
|
var offset: Long = 0
|
||||||
|
|
||||||
|
for (chunkIndex in 0..chunkCount) {
|
||||||
|
fileRequestBody.setOffset(offset)
|
||||||
|
|
||||||
|
if (cancellationRequested.get()) {
|
||||||
|
result = RemoteOperationResult<Unit>(OperationCancelledException())
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
putMethod = PutMethod(URL(uriPrefix + File.separator + chunkIndex), fileRequestBody).apply {
|
||||||
|
if (chunkIndex == chunkCount - 1) {
|
||||||
|
// Added a high timeout to the last chunk due to when the last chunk
|
||||||
|
// arrives to the server with the last PUT, all chunks get assembled
|
||||||
|
// within that PHP request, so last one takes longer.
|
||||||
|
setReadTimeout(LAST_CHUNK_TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(putMethod)
|
||||||
|
|
||||||
|
Timber.d("Upload of $localPath to $remotePath, chunk index $chunkIndex, count $chunkCount, HTTP result status $status")
|
||||||
|
|
||||||
|
if (isSuccess(status)) {
|
||||||
|
result = RemoteOperationResult<Unit>(ResultCode.OK)
|
||||||
|
} else {
|
||||||
|
result = RemoteOperationResult<Unit>(putMethod)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset += CHUNK_SIZE
|
||||||
|
}
|
||||||
|
channel.close()
|
||||||
|
raf.close()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val CHUNK_SIZE = 10_240_000L // 10 MB
|
||||||
|
private const val LAST_CHUNK_TIMEOUT = 900_000 // 15 mins.
|
||||||
|
}
|
||||||
|
}
|
@ -1,134 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files.chunks;
|
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
|
||||||
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod;
|
|
||||||
import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody;
|
|
||||||
import com.owncloud.android.lib.common.operations.OperationCancelledException;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
|
||||||
import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
|
|
||||||
import okhttp3.MediaType;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static com.owncloud.android.lib.common.http.HttpConstants.IF_MATCH_HEADER;
|
|
||||||
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote operation performing the chunked upload of a remote file to the ownCloud server.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation {
|
|
||||||
|
|
||||||
public static final long CHUNK_SIZE = 1024000;
|
|
||||||
private static final int LAST_CHUNK_TIMEOUT = 900000; //15 mins.
|
|
||||||
|
|
||||||
private String mTransferId;
|
|
||||||
|
|
||||||
public ChunkedUploadRemoteFileOperation(String transferId, String localPath, String remotePath, String mimeType,
|
|
||||||
String requiredEtag, String fileLastModifTimestamp) {
|
|
||||||
super(localPath, remotePath, mimeType, requiredEtag, fileLastModifTimestamp);
|
|
||||||
mTransferId = transferId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult uploadFile(OwnCloudClient client) throws Exception {
|
|
||||||
int status;
|
|
||||||
RemoteOperationResult result = null;
|
|
||||||
FileChannel channel;
|
|
||||||
RandomAccessFile raf;
|
|
||||||
|
|
||||||
File fileToUpload = new File(mLocalPath);
|
|
||||||
MediaType mediaType = MediaType.parse(mMimeType);
|
|
||||||
|
|
||||||
raf = new RandomAccessFile(fileToUpload, "r");
|
|
||||||
channel = raf.getChannel();
|
|
||||||
|
|
||||||
mFileRequestBody = new ChunkFromFileRequestBody(fileToUpload, mediaType, channel, CHUNK_SIZE);
|
|
||||||
|
|
||||||
synchronized (mDataTransferListeners) {
|
|
||||||
mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners);
|
|
||||||
}
|
|
||||||
|
|
||||||
long offset = 0;
|
|
||||||
String uriPrefix = client.getUploadsWebDavUri() + File.separator + mTransferId;
|
|
||||||
long totalLength = fileToUpload.length();
|
|
||||||
long chunkCount = (long) Math.ceil((double) totalLength / CHUNK_SIZE);
|
|
||||||
|
|
||||||
for (int chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++, offset += CHUNK_SIZE) {
|
|
||||||
|
|
||||||
((ChunkFromFileRequestBody) mFileRequestBody).setOffset(offset);
|
|
||||||
|
|
||||||
if (mCancellationRequested.get()) {
|
|
||||||
result = new RemoteOperationResult<>(new OperationCancelledException());
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
mPutMethod = new PutMethod(new URL(uriPrefix + File.separator + chunkIndex), mFileRequestBody);
|
|
||||||
|
|
||||||
if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
|
|
||||||
mPutMethod.addRequestHeader(IF_MATCH_HEADER, "\"" + mRequiredEtag + "\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chunkIndex == chunkCount - 1) {
|
|
||||||
// Added a high timeout to the last chunk due to when the last chunk
|
|
||||||
// arrives to the server with the last PUT, all chunks get assembled
|
|
||||||
// within that PHP request, so last one takes longer.
|
|
||||||
mPutMethod.setReadTimeout(LAST_CHUNK_TIMEOUT, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
status = client.executeHttpMethod(mPutMethod);
|
|
||||||
|
|
||||||
Timber.d("Upload of " + mLocalPath + " to " + mRemotePath +
|
|
||||||
", chunk index " + chunkIndex + ", count " + chunkCount +
|
|
||||||
", HTTP result status " + status);
|
|
||||||
|
|
||||||
if (isSuccess(status)) {
|
|
||||||
result = new RemoteOperationResult<>(OK);
|
|
||||||
} else {
|
|
||||||
result = new RemoteOperationResult<>(mPutMethod);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel != null) {
|
|
||||||
channel.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (raf != null) {
|
|
||||||
raf.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
/* ownCloud Android Library is available under MIT license
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* Copyright (C) 2021 ownCloud GmbH.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,30 +21,38 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
package com.owncloud.android.lib.resources.files.chunks
|
||||||
|
|
||||||
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.MoveRemoteFileOperation;
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod
|
||||||
|
import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote operation to move the file built from chunks after uploading it
|
* Remote operation to move the file built from chunks after uploading it
|
||||||
*
|
*
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
|
* @author Abel García de Prada
|
||||||
*/
|
*/
|
||||||
public class MoveRemoteChunksFileOperation extends MoveRemoteFileOperation {
|
class MoveRemoteChunksFileOperation(
|
||||||
|
sourceRemotePath: String,
|
||||||
|
targetRemotePath: String,
|
||||||
|
private val fileLastModificationTimestamp: String,
|
||||||
|
private val fileLength: Long
|
||||||
|
) : MoveRemoteFileOperation(
|
||||||
|
sourceRemotePath = sourceRemotePath,
|
||||||
|
targetRemotePath = targetRemotePath,
|
||||||
|
) {
|
||||||
|
|
||||||
/**
|
override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri
|
||||||
* Constructor.
|
|
||||||
*
|
override fun addRequestHeaders(moveMethod: MoveMethod) {
|
||||||
* @param srcRemotePath Remote path of the file/folder to move.
|
super.addRequestHeaders(moveMethod)
|
||||||
* @param targetRemotePath Remove path desired for the file/folder after moving it.
|
|
||||||
* @param overwrite
|
moveMethod.apply {
|
||||||
*/
|
addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, fileLastModificationTimestamp)
|
||||||
public MoveRemoteChunksFileOperation(String srcRemotePath, String targetRemotePath, boolean overwrite,
|
addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, fileLength.toString())
|
||||||
String fileLastModifTimestamp, long fileLength) {
|
}
|
||||||
super(srcRemotePath, targetRemotePath, overwrite);
|
|
||||||
moveChunkedFile = true;
|
|
||||||
mFileLastModifTimestamp = fileLastModifTimestamp;
|
|
||||||
mFileLength = fileLength;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,20 +22,11 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
package com.owncloud.android.lib.resources.files.chunks
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files.chunks;
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation
|
||||||
|
|
||||||
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation;
|
class RemoveRemoteChunksFolderOperation(remotePath: String) : RemoveRemoteFileOperation(remotePath) {
|
||||||
|
override fun getSrcWebDavUriForClient(client: OwnCloudClient): String = client.uploadsWebDavUri.toString()
|
||||||
public class RemoveRemoteChunksFolderOperation extends RemoveRemoteFileOperation {
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param remotePath RemotePath of the remote file or folder to remove from the server
|
|
||||||
*/
|
|
||||||
public RemoveRemoteChunksFolderOperation(String remotePath) {
|
|
||||||
super(remotePath);
|
|
||||||
removeChunksFolder = true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,41 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* Copyright (C) 2021 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.lib.resources.files.services
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.Service
|
||||||
|
|
||||||
|
interface ChunkService : Service {
|
||||||
|
fun removeFile(
|
||||||
|
remotePath: String
|
||||||
|
): RemoteOperationResult<Unit>
|
||||||
|
|
||||||
|
fun moveFile(
|
||||||
|
sourceRemotePath: String,
|
||||||
|
targetRemotePath: String,
|
||||||
|
fileLastModificationTimestamp: String,
|
||||||
|
fileLength: Long
|
||||||
|
): RemoteOperationResult<Unit>
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
/* ownCloud Android Library is available under MIT license
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* Copyright (C) 2023 ownCloud GmbH.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -25,7 +25,62 @@ package com.owncloud.android.lib.resources.files.services
|
|||||||
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
import com.owncloud.android.lib.resources.Service
|
import com.owncloud.android.lib.resources.Service
|
||||||
|
import com.owncloud.android.lib.resources.files.RemoteFile
|
||||||
|
|
||||||
interface FileService: Service {
|
interface FileService : Service {
|
||||||
fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean>
|
fun checkPathExistence(
|
||||||
|
path: String,
|
||||||
|
isUserLogged: Boolean,
|
||||||
|
spaceWebDavUrl: String? = null,
|
||||||
|
): RemoteOperationResult<Boolean>
|
||||||
|
|
||||||
|
fun copyFile(
|
||||||
|
sourceRemotePath: String,
|
||||||
|
targetRemotePath: String,
|
||||||
|
sourceSpaceWebDavUrl: String?,
|
||||||
|
targetSpaceWebDavUrl: String?,
|
||||||
|
replace: Boolean,
|
||||||
|
): RemoteOperationResult<String?>
|
||||||
|
|
||||||
|
fun createFolder(
|
||||||
|
remotePath: String,
|
||||||
|
createFullPath: Boolean,
|
||||||
|
isChunkFolder: Boolean = false,
|
||||||
|
spaceWebDavUrl: String? = null,
|
||||||
|
): RemoteOperationResult<Unit>
|
||||||
|
|
||||||
|
fun downloadFile(
|
||||||
|
remotePath: String,
|
||||||
|
localTempPath: String
|
||||||
|
): RemoteOperationResult<Unit>
|
||||||
|
|
||||||
|
fun moveFile(
|
||||||
|
sourceRemotePath: String,
|
||||||
|
targetRemotePath: String,
|
||||||
|
spaceWebDavUrl: String?,
|
||||||
|
replace: Boolean,
|
||||||
|
): RemoteOperationResult<Unit>
|
||||||
|
|
||||||
|
fun readFile(
|
||||||
|
remotePath: String,
|
||||||
|
spaceWebDavUrl: String? = null,
|
||||||
|
): RemoteOperationResult<RemoteFile>
|
||||||
|
|
||||||
|
fun refreshFolder(
|
||||||
|
remotePath: String,
|
||||||
|
spaceWebDavUrl: String? = null,
|
||||||
|
): RemoteOperationResult<ArrayList<RemoteFile>>
|
||||||
|
|
||||||
|
fun removeFile(
|
||||||
|
remotePath: String,
|
||||||
|
spaceWebDavUrl: String? = null,
|
||||||
|
): RemoteOperationResult<Unit>
|
||||||
|
|
||||||
|
fun renameFile(
|
||||||
|
oldName: String,
|
||||||
|
oldRemotePath: String,
|
||||||
|
newName: String,
|
||||||
|
isFolder: Boolean,
|
||||||
|
spaceWebDavUrl: String? = null,
|
||||||
|
): RemoteOperationResult<Unit>
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
/* ownCloud Android Library is available under MIT license
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* Copyright (C) 2021 ownCloud GmbH.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -21,29 +21,30 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.resources.files
|
|
||||||
|
|
||||||
import android.net.Uri
|
package com.owncloud.android.lib.resources.files.services.implementation
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
import okhttp3.HttpUrl
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.files.chunks.MoveRemoteChunksFileOperation
|
||||||
|
import com.owncloud.android.lib.resources.files.chunks.RemoveRemoteChunksFolderOperation
|
||||||
|
import com.owncloud.android.lib.resources.files.services.ChunkService
|
||||||
|
|
||||||
class RemoteFileUtil {
|
class OCChunkService(override val client: OwnCloudClient) : ChunkService {
|
||||||
companion object {
|
|
||||||
/**
|
override fun removeFile(remotePath: String): RemoteOperationResult<Unit> =
|
||||||
* Retrieves a relative path from a remote file url
|
RemoveRemoteChunksFolderOperation(remotePath = remotePath).execute(client)
|
||||||
*
|
|
||||||
*
|
override fun moveFile(
|
||||||
* Example: url:port/remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt
|
sourceRemotePath: String,
|
||||||
*
|
targetRemotePath: String,
|
||||||
* @param url remote file url
|
fileLastModificationTimestamp: String,
|
||||||
* @param userId file owner
|
fileLength: Long
|
||||||
* @return remote relative path of the file
|
): RemoteOperationResult<Unit> =
|
||||||
*/
|
MoveRemoteChunksFileOperation(
|
||||||
fun getRemotePathFromUrl(url: HttpUrl, userId: String): String? {
|
sourceRemotePath = sourceRemotePath,
|
||||||
val davFilesPath = OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId
|
targetRemotePath = targetRemotePath,
|
||||||
val absoluteDavPath = Uri.decode(url.encodedPath)
|
fileLastModificationTimestamp = fileLastModificationTimestamp,
|
||||||
val pathToOc = absoluteDavPath.split(davFilesPath)[0]
|
fileLength = fileLength,
|
||||||
return absoluteDavPath.replace(pathToOc + davFilesPath, "")
|
).execute(client)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,34 +1,143 @@
|
|||||||
/**
|
/* ownCloud Android Library is available under MIT license
|
||||||
* ownCloud Android client application
|
* Copyright (C) 2023 ownCloud GmbH.
|
||||||
*
|
*
|
||||||
* @author Abel García de Prada
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* it under the terms of the GNU General Public License version 2,
|
* all copies or substantial portions of the Software.
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
* GNU General Public License for more details.
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.files.services.implementation
|
package com.owncloud.android.lib.resources.files.services.implementation
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
|
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
|
||||||
|
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.MoveRemoteFileOperation
|
||||||
|
import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation
|
||||||
|
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation
|
||||||
|
import com.owncloud.android.lib.resources.files.RemoteFile
|
||||||
|
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation
|
||||||
|
import com.owncloud.android.lib.resources.files.RenameRemoteFileOperation
|
||||||
import com.owncloud.android.lib.resources.files.services.FileService
|
import com.owncloud.android.lib.resources.files.services.FileService
|
||||||
|
|
||||||
class OCFileService(override val client: OwnCloudClient) :
|
class OCFileService(override val client: OwnCloudClient) : FileService {
|
||||||
FileService {
|
override fun checkPathExistence(
|
||||||
override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean> =
|
path: String,
|
||||||
|
isUserLogged: Boolean,
|
||||||
|
spaceWebDavUrl: String?,
|
||||||
|
): RemoteOperationResult<Boolean> =
|
||||||
CheckPathExistenceRemoteOperation(
|
CheckPathExistenceRemoteOperation(
|
||||||
remotePath = path,
|
remotePath = path,
|
||||||
isUserLogged = isUserLogged
|
isUserLoggedIn = isUserLogged,
|
||||||
|
spaceWebDavUrl = spaceWebDavUrl,
|
||||||
|
).execute(client)
|
||||||
|
|
||||||
|
override fun copyFile(
|
||||||
|
sourceRemotePath: String,
|
||||||
|
targetRemotePath: String,
|
||||||
|
sourceSpaceWebDavUrl: String?,
|
||||||
|
targetSpaceWebDavUrl: String?,
|
||||||
|
replace: Boolean,
|
||||||
|
): RemoteOperationResult<String?> =
|
||||||
|
CopyRemoteFileOperation(
|
||||||
|
sourceRemotePath = sourceRemotePath,
|
||||||
|
targetRemotePath = targetRemotePath,
|
||||||
|
sourceSpaceWebDavUrl = sourceSpaceWebDavUrl,
|
||||||
|
targetSpaceWebDavUrl = targetSpaceWebDavUrl,
|
||||||
|
forceOverride = replace,
|
||||||
|
).execute(client)
|
||||||
|
|
||||||
|
override fun createFolder(
|
||||||
|
remotePath: String,
|
||||||
|
createFullPath: Boolean,
|
||||||
|
isChunkFolder: Boolean,
|
||||||
|
spaceWebDavUrl: String?,
|
||||||
|
): RemoteOperationResult<Unit> =
|
||||||
|
CreateRemoteFolderOperation(
|
||||||
|
remotePath = remotePath,
|
||||||
|
createFullPath = createFullPath,
|
||||||
|
isChunksFolder = isChunkFolder,
|
||||||
|
spaceWebDavUrl = spaceWebDavUrl,
|
||||||
|
).execute(client)
|
||||||
|
|
||||||
|
override fun downloadFile(
|
||||||
|
remotePath: String,
|
||||||
|
localTempPath: String
|
||||||
|
): RemoteOperationResult<Unit> =
|
||||||
|
DownloadRemoteFileOperation(
|
||||||
|
remotePath = remotePath,
|
||||||
|
localFolderPath = localTempPath
|
||||||
|
).execute(client)
|
||||||
|
|
||||||
|
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,
|
||||||
|
spaceWebDavUrl: String?,
|
||||||
|
): RemoteOperationResult<RemoteFile> =
|
||||||
|
ReadRemoteFileOperation(
|
||||||
|
remotePath = remotePath,
|
||||||
|
spaceWebDavUrl = spaceWebDavUrl,
|
||||||
|
).execute(client)
|
||||||
|
|
||||||
|
override fun refreshFolder(
|
||||||
|
remotePath: String,
|
||||||
|
spaceWebDavUrl: String?,
|
||||||
|
): RemoteOperationResult<ArrayList<RemoteFile>> =
|
||||||
|
ReadRemoteFolderOperation(
|
||||||
|
remotePath = remotePath,
|
||||||
|
spaceWebDavUrl = spaceWebDavUrl,
|
||||||
|
).execute(client)
|
||||||
|
|
||||||
|
override fun removeFile(
|
||||||
|
remotePath: String,
|
||||||
|
spaceWebDavUrl: String?,
|
||||||
|
): RemoteOperationResult<Unit> =
|
||||||
|
RemoveRemoteFileOperation(
|
||||||
|
remotePath = remotePath,
|
||||||
|
spaceWebDavUrl = spaceWebDavUrl,
|
||||||
|
).execute(client)
|
||||||
|
|
||||||
|
override fun renameFile(
|
||||||
|
oldName: String,
|
||||||
|
oldRemotePath: String,
|
||||||
|
newName: String,
|
||||||
|
isFolder: Boolean,
|
||||||
|
spaceWebDavUrl: String?,
|
||||||
|
): RemoteOperationResult<Unit> =
|
||||||
|
RenameRemoteFileOperation(
|
||||||
|
oldName = oldName,
|
||||||
|
oldRemotePath = oldRemotePath,
|
||||||
|
newName = newName,
|
||||||
|
isFolder = isFolder,
|
||||||
|
spaceWebDavUrl = spaceWebDavUrl,
|
||||||
).execute(client)
|
).execute(client)
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@ class GetOIDCDiscoveryRemoteOperation : RemoteOperation<OIDCDiscoveryResponse>()
|
|||||||
addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMethod.followRedirects = true
|
||||||
val status = client.executeHttpMethod(getMethod)
|
val status = client.executeHttpMethod(getMethod)
|
||||||
|
|
||||||
val responseBody = getMethod.getResponseBodyAsString()
|
val responseBody = getMethod.getResponseBodyAsString()
|
||||||
|
@ -39,7 +39,8 @@ sealed class TokenRequestParams(
|
|||||||
clientAuth: String,
|
clientAuth: String,
|
||||||
grantType: String,
|
grantType: String,
|
||||||
val authorizationCode: String,
|
val authorizationCode: String,
|
||||||
val redirectUri: String
|
val redirectUri: String,
|
||||||
|
val codeVerifier: String,
|
||||||
) : TokenRequestParams(tokenEndpoint, clientAuth, grantType) {
|
) : TokenRequestParams(tokenEndpoint, clientAuth, grantType) {
|
||||||
|
|
||||||
override fun toRequestBody(): RequestBody =
|
override fun toRequestBody(): RequestBody =
|
||||||
@ -47,6 +48,7 @@ sealed class TokenRequestParams(
|
|||||||
.add(HttpConstants.OAUTH_HEADER_AUTHORIZATION_CODE, authorizationCode)
|
.add(HttpConstants.OAUTH_HEADER_AUTHORIZATION_CODE, authorizationCode)
|
||||||
.add(HttpConstants.OAUTH_HEADER_GRANT_TYPE, grantType)
|
.add(HttpConstants.OAUTH_HEADER_GRANT_TYPE, grantType)
|
||||||
.add(HttpConstants.OAUTH_HEADER_REDIRECT_URI, redirectUri)
|
.add(HttpConstants.OAUTH_HEADER_REDIRECT_URI, redirectUri)
|
||||||
|
.add(HttpConstants.OAUTH_HEADER_CODE_VERIFIER, codeVerifier)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,13 +31,13 @@ import com.squareup.moshi.JsonClass
|
|||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class OIDCDiscoveryResponse(
|
data class OIDCDiscoveryResponse(
|
||||||
val authorization_endpoint: String,
|
val authorization_endpoint: String,
|
||||||
val check_session_iframe: String,
|
val check_session_iframe: String?,
|
||||||
val end_session_endpoint: String,
|
val end_session_endpoint: String?,
|
||||||
val issuer: String,
|
val issuer: String,
|
||||||
val registration_endpoint: String,
|
val registration_endpoint: String?,
|
||||||
val response_types_supported: List<String>,
|
val response_types_supported: List<String>,
|
||||||
val scopes_supported: List<String>,
|
val scopes_supported: List<String>?,
|
||||||
val token_endpoint: String,
|
val token_endpoint: String,
|
||||||
val token_endpoint_auth_methods_supported: List<String>,
|
val token_endpoint_auth_methods_supported: List<String>?,
|
||||||
val userinfo_endpoint: String,
|
val userinfo_endpoint: String?,
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
* @author masensio
|
* @author masensio
|
||||||
* @author David A. Velasco
|
* @author David A. Velasco
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
* Copyright (C) 2020 ownCloud GmbH
|
* @author Fernando Sanz Velasco
|
||||||
|
* Copyright (C) 2021 ownCloud GmbH
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -27,14 +28,23 @@
|
|||||||
|
|
||||||
package com.owncloud.android.lib.resources.shares
|
package com.owncloud.android.lib.resources.shares
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.PARAM_FORMAT
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.VALUE_FORMAT
|
||||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.PostMethod
|
import com.owncloud.android.lib.common.http.methods.nonwebdav.PostMethod
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.CommonOcsResponse
|
||||||
import com.owncloud.android.lib.resources.shares.RemoteShare.Companion.INIT_EXPIRATION_DATE_IN_MILLIS
|
import com.owncloud.android.lib.resources.shares.RemoteShare.Companion.INIT_EXPIRATION_DATE_IN_MILLIS
|
||||||
|
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 okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.lang.reflect.Type
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
@ -46,6 +56,7 @@ import java.util.Locale
|
|||||||
* @author masensio
|
* @author masensio
|
||||||
* @author David A. Velasco
|
* @author David A. Velasco
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
|
* @author Fernando Sanz Velasco
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,101 +81,131 @@ class CreateRemoteShareOperation(
|
|||||||
private val shareType: ShareType,
|
private val shareType: ShareType,
|
||||||
private val shareWith: String,
|
private val shareWith: String,
|
||||||
private val permissions: Int
|
private val permissions: Int
|
||||||
) : RemoteOperation<ShareParserResult>() {
|
) : RemoteOperation<ShareResponse>() {
|
||||||
|
|
||||||
var name = "" // Name to set for the public link
|
var name = "" // Name to set for the public link
|
||||||
|
|
||||||
var password: String = "" // Password to set for the public link
|
var password: String = "" // Password to set for the public link
|
||||||
|
|
||||||
var expirationDateInMillis: Long = INIT_EXPIRATION_DATE_IN_MILLIS // Expiration date to set for the public link
|
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
|
var retrieveShareDetails = false // To retrieve more info about the just created share
|
||||||
|
|
||||||
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareParserResult> {
|
private fun buildRequestUri(baseUri: Uri) =
|
||||||
var result: RemoteOperationResult<ShareParserResult>
|
baseUri.buildUpon()
|
||||||
|
.appendEncodedPath(OCS_ROUTE)
|
||||||
|
.appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT)
|
||||||
|
.build()
|
||||||
|
|
||||||
try {
|
private fun parseResponse(response: String): ShareResponse {
|
||||||
val formBodyBuilder = FormBody.Builder()
|
val moshi = Moshi.Builder().build()
|
||||||
.add(PARAM_PATH, remoteFilePath)
|
val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareItem::class.java)
|
||||||
.add(PARAM_SHARE_TYPE, shareType.value.toString())
|
val adapter: JsonAdapter<CommonOcsResponse<ShareItem>> = moshi.adapter(commonOcsType)
|
||||||
.add(PARAM_SHARE_WITH, shareWith)
|
val remoteShare = adapter.fromJson(response)?.ocs?.data?.toRemoteShare()
|
||||||
|
return ShareResponse(remoteShare?.let { listOf(it) } ?: listOf())
|
||||||
|
}
|
||||||
|
|
||||||
if (name.isNotEmpty()) {
|
private fun onResultUnsuccessful(
|
||||||
formBodyBuilder.add(PARAM_NAME, name)
|
method: PostMethod,
|
||||||
}
|
response: String?,
|
||||||
|
status: Int
|
||||||
|
): RemoteOperationResult<ShareResponse> {
|
||||||
|
Timber.e("Failed response while while creating new remote share operation ")
|
||||||
|
if (response != null) {
|
||||||
|
Timber.e("*** status code: $status; response message: $response")
|
||||||
|
} else {
|
||||||
|
Timber.e("*** status code: $status")
|
||||||
|
}
|
||||||
|
return RemoteOperationResult(method)
|
||||||
|
}
|
||||||
|
|
||||||
if (expirationDateInMillis > INIT_EXPIRATION_DATE_IN_MILLIS) {
|
private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareResponse> {
|
||||||
val dateFormat = SimpleDateFormat(FORMAT_EXPIRATION_DATE, Locale.getDefault())
|
val result = RemoteOperationResult<ShareResponse>(RemoteOperationResult.ResultCode.OK)
|
||||||
val expirationDate = Calendar.getInstance()
|
Timber.d("Successful response: $response")
|
||||||
expirationDate.timeInMillis = expirationDateInMillis
|
result.data = parseResponse(response!!)
|
||||||
val formattedExpirationDate = dateFormat.format(expirationDate.time)
|
Timber.d("*** Creating new remote share operation completed ")
|
||||||
formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (publicUpload) {
|
val emptyShare = result.data.shares.first()
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
val requestUri = client.baseUri
|
return if (retrieveShareDetails) {
|
||||||
val uriBuilder = requestUri.buildUpon()
|
// retrieve more info - PUT only returns the index of the new share
|
||||||
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH)
|
GetRemoteShareOperation(emptyShare.id).execute(client)
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val postMethod = PostMethod(URL(uriBuilder.build().toString()), formBodyBuilder.build())
|
private fun createFormBody(): FormBody {
|
||||||
|
val formBodyBuilder = FormBody.Builder()
|
||||||
|
.add(PARAM_PATH, remoteFilePath)
|
||||||
|
.add(PARAM_SHARE_TYPE, shareType.value.toString())
|
||||||
|
.add(PARAM_SHARE_WITH, shareWith)
|
||||||
|
|
||||||
postMethod.setRequestHeader(HttpConstants.CONTENT_TYPE_HEADER, HttpConstants.CONTENT_TYPE_URLENCODED_UTF8)
|
if (name.isNotEmpty()) {
|
||||||
postMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
formBodyBuilder.add(PARAM_NAME, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expirationDateInMillis > INIT_EXPIRATION_DATE_IN_MILLIS) {
|
||||||
|
val dateFormat = SimpleDateFormat(FORMAT_EXPIRATION_DATE, Locale.getDefault())
|
||||||
|
val expirationDate = Calendar.getInstance()
|
||||||
|
expirationDate.timeInMillis = expirationDateInMillis
|
||||||
|
val formattedExpirationDate = dateFormat.format(expirationDate.time)
|
||||||
|
formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.isNotEmpty()) {
|
||||||
|
formBodyBuilder.add(PARAM_PASSWORD, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RemoteShare.DEFAULT_PERMISSION != permissions) {
|
||||||
|
formBodyBuilder.add(PARAM_PERMISSIONS, permissions.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return formBodyBuilder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> {
|
||||||
|
val requestUri = buildRequestUri(client.baseUri)
|
||||||
|
|
||||||
|
val postMethod = PostMethod(URL(requestUri.toString()), createFormBody()).apply {
|
||||||
|
setRequestHeader(HttpConstants.CONTENT_TYPE_HEADER, HttpConstants.CONTENT_TYPE_URLENCODED_UTF8)
|
||||||
|
addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
val status = client.executeHttpMethod(postMethod)
|
val status = client.executeHttpMethod(postMethod)
|
||||||
|
val response = postMethod.getResponseBodyAsString()
|
||||||
val parser = ShareToRemoteOperationResultParser(
|
|
||||||
ShareXMLParser()
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isSuccess(status)) {
|
if (isSuccess(status)) {
|
||||||
parser.oneOrMoreSharesRequired = true
|
onRequestSuccessful(response)
|
||||||
parser.ownCloudVersion = client.ownCloudVersion
|
|
||||||
parser.serverBaseUri = client.baseUri
|
|
||||||
result = parser.parse(postMethod.getResponseBodyAsString())
|
|
||||||
|
|
||||||
if (result.isSuccess && retrieveShareDetails) {
|
|
||||||
// retrieve more info - POST only returns the index of the new share
|
|
||||||
val emptyShare = result.data.shares[0]
|
|
||||||
val getInfo = GetRemoteShareOperation(
|
|
||||||
emptyShare.id
|
|
||||||
)
|
|
||||||
result = getInfo.execute(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
result = parser.parse(postMethod.getResponseBodyAsString())
|
onResultUnsuccessful(postMethod, response, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
result = RemoteOperationResult(e)
|
Timber.e(e, "Exception while creating new remote share operation ")
|
||||||
Timber.e(e, "Exception while Creating New Share")
|
RemoteOperationResult(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
|
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
//OCS Route
|
||||||
|
private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/shares"
|
||||||
|
|
||||||
|
//Arguments - names
|
||||||
|
|
||||||
private const val PARAM_NAME = "name"
|
private const val PARAM_NAME = "name"
|
||||||
private const val PARAM_PASSWORD = "password"
|
|
||||||
private const val PARAM_EXPIRATION_DATE = "expireDate"
|
private const val PARAM_EXPIRATION_DATE = "expireDate"
|
||||||
private const val PARAM_PUBLIC_UPLOAD = "publicUpload"
|
|
||||||
private const val PARAM_PATH = "path"
|
private const val PARAM_PATH = "path"
|
||||||
private const val PARAM_SHARE_TYPE = "shareType"
|
private const val PARAM_SHARE_TYPE = "shareType"
|
||||||
private const val PARAM_SHARE_WITH = "shareWith"
|
private const val PARAM_SHARE_WITH = "shareWith"
|
||||||
|
private const val PARAM_PASSWORD = "password"
|
||||||
private const val PARAM_PERMISSIONS = "permissions"
|
private const val PARAM_PERMISSIONS = "permissions"
|
||||||
|
|
||||||
|
//Arguments - constant values
|
||||||
private const val FORMAT_EXPIRATION_DATE = "yyyy-MM-dd"
|
private const val FORMAT_EXPIRATION_DATE = "yyyy-MM-dd"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author David González Verdugo
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* 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.shares;
|
|
||||||
|
|
||||||
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 timber.log.Timber;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the data about a Share resource, known its remote ID.
|
|
||||||
*
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class GetRemoteShareOperation extends RemoteOperation<ShareParserResult> {
|
|
||||||
|
|
||||||
private String mRemoteId;
|
|
||||||
|
|
||||||
public GetRemoteShareOperation(String remoteId) {
|
|
||||||
mRemoteId = remoteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RemoteOperationResult run(OwnCloudClient client) {
|
|
||||||
RemoteOperationResult<ShareParserResult> result;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Uri requestUri = client.getBaseUri();
|
|
||||||
Uri.Builder uriBuilder = requestUri.buildUpon();
|
|
||||||
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH);
|
|
||||||
uriBuilder.appendEncodedPath(mRemoteId);
|
|
||||||
|
|
||||||
GetMethod getMethod = new GetMethod(new URL(uriBuilder.build().toString()));
|
|
||||||
getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE);
|
|
||||||
|
|
||||||
int status = client.executeHttpMethod(getMethod);
|
|
||||||
|
|
||||||
if (isSuccess(status)) {
|
|
||||||
// Parse xml response and obtain the list of shares
|
|
||||||
ShareToRemoteOperationResultParser parser = new ShareToRemoteOperationResultParser(
|
|
||||||
new ShareXMLParser()
|
|
||||||
);
|
|
||||||
parser.setOneOrMoreSharesRequired(true);
|
|
||||||
parser.setOwnCloudVersion(client.getOwnCloudVersion());
|
|
||||||
parser.setServerBaseUri(client.getBaseUri());
|
|
||||||
result = parser.parse(getMethod.getResponseBodyAsString());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
result = new RemoteOperationResult<>(getMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
result = new RemoteOperationResult<>(e);
|
|
||||||
Timber.e(e, "Exception while getting remote shares");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSuccess(int status) {
|
|
||||||
return (status == HttpConstants.HTTP_OK);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,116 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
* @author Fernando Sanz Velasco
|
||||||
|
* Copyright (C) 2021 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.shares
|
||||||
|
|
||||||
|
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.HttpConstants.PARAM_FORMAT
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.VALUE_FORMAT
|
||||||
|
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.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
|
||||||
|
|
||||||
|
class GetRemoteShareOperation(private val remoteId: String) : RemoteOperation<ShareResponse>() {
|
||||||
|
|
||||||
|
private fun buildRequestUri(baseUri: Uri) =
|
||||||
|
baseUri.buildUpon()
|
||||||
|
.appendEncodedPath(OCS_ROUTE)
|
||||||
|
.appendEncodedPath(remoteId)
|
||||||
|
.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: GetMethod,
|
||||||
|
response: String?,
|
||||||
|
status: Int
|
||||||
|
): RemoteOperationResult<ShareResponse> {
|
||||||
|
Timber.e("Failed response while while getting remote shares ")
|
||||||
|
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<ShareResponse> {
|
||||||
|
val result = RemoteOperationResult<ShareResponse>(RemoteOperationResult.ResultCode.OK)
|
||||||
|
Timber.d("Successful response: $response")
|
||||||
|
result.data = parseResponse(response!!)
|
||||||
|
Timber.d("*** Get Users or groups completed ")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> {
|
||||||
|
val requestUri = buildRequestUri(client.baseUri)
|
||||||
|
|
||||||
|
val getMethod = GetMethod(URL(requestUri.toString())).apply {
|
||||||
|
addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val status = client.executeHttpMethod(getMethod)
|
||||||
|
val response = getMethod.getResponseBodyAsString()
|
||||||
|
|
||||||
|
if (!isSuccess(status)) {
|
||||||
|
onResultUnsuccessful(getMethod, response, status)
|
||||||
|
} else {
|
||||||
|
onRequestSuccessful(response)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Exception while getting remote shares")
|
||||||
|
RemoteOperationResult(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
//OCS Route
|
||||||
|
private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/shares"
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,8 @@ package com.owncloud.android.lib.resources.shares
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.PARAM_FORMAT
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.VALUE_FORMAT
|
||||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
|
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.RemoteOperation
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
@ -97,11 +99,11 @@ class GetRemoteShareesOperation
|
|||||||
.appendQueryParameter(PARAM_PER_PAGE, perPage.toString())
|
.appendQueryParameter(PARAM_PER_PAGE, perPage.toString())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private fun parseResponse(response: String): ShareeOcsResponse? {
|
private fun parseResponse(response: String?): ShareeOcsResponse? {
|
||||||
val moshi = Moshi.Builder().build()
|
val moshi = Moshi.Builder().build()
|
||||||
val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java)
|
val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java)
|
||||||
val adapter: JsonAdapter<CommonOcsResponse<ShareeOcsResponse>> = moshi.adapter(type)
|
val adapter: JsonAdapter<CommonOcsResponse<ShareeOcsResponse>> = moshi.adapter(type)
|
||||||
return adapter.fromJson(response)!!.ocs.data
|
return response?.let { adapter.fromJson(it)?.ocs?.data }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onResultUnsuccessful(
|
private fun onResultUnsuccessful(
|
||||||
@ -121,7 +123,7 @@ class GetRemoteShareesOperation
|
|||||||
private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareeOcsResponse> {
|
private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareeOcsResponse> {
|
||||||
val result = RemoteOperationResult<ShareeOcsResponse>(OK)
|
val result = RemoteOperationResult<ShareeOcsResponse>(OK)
|
||||||
Timber.d("Successful response: $response")
|
Timber.d("Successful response: $response")
|
||||||
result.data = parseResponse(response!!)
|
result.data = parseResponse(response)
|
||||||
Timber.d("*** Get Users or groups completed ")
|
Timber.d("*** Get Users or groups completed ")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -136,10 +138,10 @@ class GetRemoteShareesOperation
|
|||||||
val status = client.executeHttpMethod(getMethod)
|
val status = client.executeHttpMethod(getMethod)
|
||||||
val response = getMethod.getResponseBodyAsString()
|
val response = getMethod.getResponseBodyAsString()
|
||||||
|
|
||||||
if (!isSuccess(status)) {
|
if (isSuccess(status)) {
|
||||||
onResultUnsuccessful(getMethod, response, status)
|
|
||||||
} else {
|
|
||||||
onRequestSuccessful(response)
|
onRequestSuccessful(response)
|
||||||
|
} else {
|
||||||
|
onResultUnsuccessful(getMethod, response, status)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "Exception while getting users/groups")
|
Timber.e(e, "Exception while getting users/groups")
|
||||||
@ -155,14 +157,12 @@ class GetRemoteShareesOperation
|
|||||||
private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/sharees" // from OC 8.2
|
private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/sharees" // from OC 8.2
|
||||||
|
|
||||||
// Arguments - names
|
// Arguments - names
|
||||||
private const val PARAM_FORMAT = "format"
|
|
||||||
private const val PARAM_ITEM_TYPE = "itemType"
|
private const val PARAM_ITEM_TYPE = "itemType"
|
||||||
private const val PARAM_SEARCH = "search"
|
private const val PARAM_SEARCH = "search"
|
||||||
private const val PARAM_PAGE = "page" // default = 1
|
private const val PARAM_PAGE = "page" // default = 1
|
||||||
private const val PARAM_PER_PAGE = "perPage" // default = 200
|
private const val PARAM_PER_PAGE = "perPage" // default = 200
|
||||||
|
|
||||||
// Arguments - constant values
|
// Arguments - constant values
|
||||||
private const val VALUE_FORMAT = "json"
|
|
||||||
private const val VALUE_ITEM_TYPE = "file" // to get the server search for users / groups
|
private const val VALUE_ITEM_TYPE = "file" // to get the server search for users / groups
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
* @author masensio
|
* @author masensio
|
||||||
* @author David A. Velasco
|
* @author David A. Velasco
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* @author Fernando Sanz Velasco
|
||||||
|
* Copyright (C) 2021 ownCloud GmbH
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -27,12 +28,21 @@
|
|||||||
|
|
||||||
package com.owncloud.android.lib.resources.shares
|
package com.owncloud.android.lib.resources.shares
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.PARAM_FORMAT
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.VALUE_FORMAT
|
||||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
|
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.RemoteOperation
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
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 timber.log.Timber
|
||||||
|
import java.lang.reflect.Type
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,6 +53,7 @@ import java.net.URL
|
|||||||
* @author masensio
|
* @author masensio
|
||||||
* @author David A. Velasco
|
* @author David A. Velasco
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
|
* @author Fernando Sanz Velasco
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,52 +70,82 @@ class GetRemoteSharesForFileOperation(
|
|||||||
private val remoteFilePath: String,
|
private val remoteFilePath: String,
|
||||||
private val reshares: Boolean,
|
private val reshares: Boolean,
|
||||||
private val subfiles: Boolean
|
private val subfiles: Boolean
|
||||||
) : RemoteOperation<ShareParserResult>() {
|
) : RemoteOperation<ShareResponse>() {
|
||||||
|
|
||||||
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareParserResult> {
|
private fun buildRequestUri(baseUri: Uri) =
|
||||||
var result: RemoteOperationResult<ShareParserResult>
|
baseUri.buildUpon()
|
||||||
|
.appendEncodedPath(OCS_ROUTE)
|
||||||
|
.appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT)
|
||||||
|
.appendQueryParameter(PARAM_PATH, remoteFilePath)
|
||||||
|
.appendQueryParameter(PARAM_RESHARES, reshares.toString())
|
||||||
|
.appendQueryParameter(PARAM_SUBFILES, subfiles.toString())
|
||||||
|
.build()
|
||||||
|
|
||||||
try {
|
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()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val requestUri = client.baseUri
|
private fun onResultUnsuccessful(
|
||||||
val uriBuilder = requestUri.buildUpon()
|
method: GetMethod,
|
||||||
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH)
|
response: String?,
|
||||||
uriBuilder.appendQueryParameter(PARAM_PATH, remoteFilePath)
|
status: Int
|
||||||
uriBuilder.appendQueryParameter(PARAM_RESHARES, reshares.toString())
|
): RemoteOperationResult<ShareResponse> {
|
||||||
uriBuilder.appendQueryParameter(PARAM_SUBFILES, subfiles.toString())
|
Timber.e("Failed response while while getting remote shares for file operation ")
|
||||||
|
if (response != null) {
|
||||||
|
Timber.e("*** status code: $status; response message: $response")
|
||||||
|
} else {
|
||||||
|
Timber.e("*** status code: $status")
|
||||||
|
}
|
||||||
|
return RemoteOperationResult(method)
|
||||||
|
}
|
||||||
|
|
||||||
val getMethod = GetMethod(URL(uriBuilder.build().toString()))
|
private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareResponse> {
|
||||||
|
val result = RemoteOperationResult<ShareResponse>(RemoteOperationResult.ResultCode.OK)
|
||||||
|
Timber.d("Successful response: $response")
|
||||||
|
result.data = parseResponse(response!!)
|
||||||
|
Timber.d("*** Getting remote shares for file completed ")
|
||||||
|
Timber.d("Got ${result.data.shares.size} shares")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> {
|
||||||
|
val requestUri = buildRequestUri(client.baseUri)
|
||||||
|
|
||||||
val status = client.executeHttpMethod(getMethod)
|
val getMethod = GetMethod(URL(requestUri.toString())).apply {
|
||||||
|
addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
||||||
if (isSuccess(status)) {
|
|
||||||
// Parse xml response and obtain the list of shares
|
|
||||||
val parser = ShareToRemoteOperationResultParser(
|
|
||||||
ShareXMLParser()
|
|
||||||
)
|
|
||||||
parser.ownCloudVersion = client.ownCloudVersion
|
|
||||||
parser.serverBaseUri = client.baseUri
|
|
||||||
result = parser.parse(getMethod.getResponseBodyAsString())
|
|
||||||
|
|
||||||
if (result.isSuccess) {
|
|
||||||
Timber.d("Got ${result.data.shares.size} shares")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = RemoteOperationResult(getMethod)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
result = RemoteOperationResult(e)
|
|
||||||
Timber.e(e, "Exception while getting shares")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
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 for file operation")
|
||||||
|
RemoteOperationResult(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
|
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
//OCS Route
|
||||||
|
private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/shares"
|
||||||
|
|
||||||
|
//Arguments - names
|
||||||
private const val PARAM_PATH = "path"
|
private const val PARAM_PATH = "path"
|
||||||
private const val PARAM_RESHARES = "reshares"
|
private const val PARAM_RESHARES = "reshares"
|
||||||
private const val PARAM_SUBFILES = "subfiles"
|
private const val PARAM_SUBFILES = "subfiles"
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
package com.owncloud.android.lib.resources.shares
|
package com.owncloud.android.lib.resources.shares
|
||||||
|
|
||||||
import java.io.File
|
import com.owncloud.android.lib.resources.shares.responses.ItemType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the data of a Share from the Share API
|
* Contains the data of a Share from the Share API
|
||||||
@ -38,6 +38,7 @@ data class RemoteShare(
|
|||||||
var shareWith: String = "",
|
var shareWith: String = "",
|
||||||
var path: String = "",
|
var path: String = "",
|
||||||
var token: String = "",
|
var token: String = "",
|
||||||
|
var itemType: String = "",
|
||||||
var sharedWithDisplayName: String = "",
|
var sharedWithDisplayName: String = "",
|
||||||
var sharedWithAdditionalInfo: String = "",
|
var sharedWithAdditionalInfo: String = "",
|
||||||
var name: String = "",
|
var name: String = "",
|
||||||
@ -46,7 +47,7 @@ data class RemoteShare(
|
|||||||
var permissions: Int = DEFAULT_PERMISSION,
|
var permissions: Int = DEFAULT_PERMISSION,
|
||||||
var sharedDate: Long = INIT_SHARED_DATE,
|
var sharedDate: Long = INIT_SHARED_DATE,
|
||||||
var expirationDate: Long = INIT_EXPIRATION_DATE_IN_MILLIS,
|
var expirationDate: Long = INIT_EXPIRATION_DATE_IN_MILLIS,
|
||||||
var isFolder: Boolean = path.endsWith(File.separator)
|
var isFolder: Boolean = (itemType == ItemType.FOLDER.fileValue)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
* @author masensio
|
* @author masensio
|
||||||
* @author David A. Velasco
|
* @author David A. Velasco
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* @author Fernando Sanz Velasco
|
||||||
|
* Copyright (C) 2021 ownCloud GmbH
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -27,8 +28,11 @@
|
|||||||
|
|
||||||
package com.owncloud.android.lib.resources.shares
|
package com.owncloud.android.lib.resources.shares
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.PARAM_FORMAT
|
||||||
|
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.http.methods.nonwebdav.DeleteMethod
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
@ -41,53 +45,66 @@ import java.net.URL
|
|||||||
* @author masensio
|
* @author masensio
|
||||||
* @author David A. Velasco
|
* @author David A. Velasco
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
*/
|
* @author Fernando Sanz Velasco
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
*
|
||||||
* @param remoteShareId Share ID
|
* @param remoteShareId Share ID
|
||||||
*/
|
*/
|
||||||
class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation<ShareParserResult>() {
|
class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation<Unit>() {
|
||||||
|
|
||||||
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareParserResult> {
|
private fun buildRequestUri(baseUri: Uri) =
|
||||||
var result: RemoteOperationResult<ShareParserResult>
|
baseUri.buildUpon()
|
||||||
|
.appendEncodedPath(OCS_ROUTE)
|
||||||
|
.appendEncodedPath(remoteShareId)
|
||||||
|
.appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT)
|
||||||
|
.build()
|
||||||
|
|
||||||
try {
|
private fun onResultUnsuccessful(
|
||||||
val requestUri = client.baseUri
|
method: DeleteMethod,
|
||||||
val uriBuilder = requestUri.buildUpon()
|
response: String?,
|
||||||
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH)
|
status: Int
|
||||||
uriBuilder.appendEncodedPath(remoteShareId)
|
): RemoteOperationResult<Unit> {
|
||||||
|
Timber.e("Failed response while removing share ")
|
||||||
val deleteMethod = DeleteMethod(
|
if (response != null) {
|
||||||
URL(uriBuilder.build().toString())
|
Timber.e("*** status code: $status; response message: $response")
|
||||||
)
|
} else {
|
||||||
|
Timber.e("*** status code: $status")
|
||||||
deleteMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
|
||||||
|
|
||||||
val status = client.executeHttpMethod(deleteMethod)
|
|
||||||
|
|
||||||
if (isSuccess(status)) {
|
|
||||||
|
|
||||||
// Parse xml response and obtain the list of shares
|
|
||||||
val parser = ShareToRemoteOperationResultParser(
|
|
||||||
ShareXMLParser()
|
|
||||||
)
|
|
||||||
result = parser.parse(deleteMethod.getResponseBodyAsString())
|
|
||||||
|
|
||||||
Timber.d("Unshare $remoteShareId: ${result.logMessage}")
|
|
||||||
|
|
||||||
} else {
|
|
||||||
result = RemoteOperationResult(deleteMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e: Exception) {
|
|
||||||
result = RemoteOperationResult(e)
|
|
||||||
Timber.e(e, "Unshare Link Exception ${result.logMessage}")
|
|
||||||
}
|
}
|
||||||
|
return RemoteOperationResult(method)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onRequestSuccessful(response: String?): RemoteOperationResult<Unit> {
|
||||||
|
val result = RemoteOperationResult<Unit>(RemoteOperationResult.ResultCode.OK)
|
||||||
|
Timber.d("Successful response: $response")
|
||||||
|
Timber.d("*** Unshare link completed ")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||||
|
val requestUri = buildRequestUri(client.baseUri)
|
||||||
|
|
||||||
|
val deleteMethod = DeleteMethod(URL(requestUri.toString())).apply {
|
||||||
|
addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val status = client.executeHttpMethod(deleteMethod)
|
||||||
|
val response = deleteMethod.getResponseBodyAsString()
|
||||||
|
|
||||||
|
if (isSuccess(status)) {
|
||||||
|
onRequestSuccessful(response)
|
||||||
|
} else {
|
||||||
|
onResultUnsuccessful(deleteMethod, response, status)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Exception while unshare link")
|
||||||
|
RemoteOperationResult(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
|
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// OCS Route
|
||||||
|
private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/shares"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,4 +25,4 @@
|
|||||||
|
|
||||||
package com.owncloud.android.lib.resources.shares
|
package com.owncloud.android.lib.resources.shares
|
||||||
|
|
||||||
class ShareParserResult(val shares: List<RemoteShare>)
|
data class ShareResponse(val shares: List<RemoteShare>)
|
@ -1,120 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* @author David A. Velasco
|
|
||||||
* @author David González Verdugo
|
|
||||||
* @author Christian Schabesberger
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.shares
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
|
||||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion
|
|
||||||
import org.xmlpull.v1.XmlPullParserException
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.IOException
|
|
||||||
import java.util.ArrayList
|
|
||||||
|
|
||||||
class ShareToRemoteOperationResultParser(private var shareXmlParser: ShareXMLParser?) {
|
|
||||||
var oneOrMoreSharesRequired = false
|
|
||||||
var ownCloudVersion: OwnCloudVersion? = null
|
|
||||||
var serverBaseUri: Uri? = null
|
|
||||||
|
|
||||||
fun parse(serverResponse: String?): RemoteOperationResult<ShareParserResult> {
|
|
||||||
if (serverResponse.isNullOrEmpty()) {
|
|
||||||
return RemoteOperationResult(RemoteOperationResult.ResultCode.WRONG_SERVER_RESPONSE)
|
|
||||||
}
|
|
||||||
|
|
||||||
var result: RemoteOperationResult<ShareParserResult>
|
|
||||||
val resultData: List<RemoteShare>?
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Parse xml response and obtain the list of shares
|
|
||||||
val byteArrayServerResponse = ByteArrayInputStream(serverResponse.toByteArray())
|
|
||||||
if (shareXmlParser == null) {
|
|
||||||
Timber.w("No ShareXmlParser provided, creating new instance")
|
|
||||||
shareXmlParser = ShareXMLParser()
|
|
||||||
}
|
|
||||||
val shares = shareXmlParser?.parseXMLResponse(byteArrayServerResponse)
|
|
||||||
|
|
||||||
when {
|
|
||||||
shareXmlParser?.isSuccess!! -> {
|
|
||||||
if (!shares.isNullOrEmpty() || !oneOrMoreSharesRequired) {
|
|
||||||
result = RemoteOperationResult(RemoteOperationResult.ResultCode.OK)
|
|
||||||
|
|
||||||
resultData = shares?.map { share ->
|
|
||||||
if (share.shareType != ShareType.PUBLIC_LINK ||
|
|
||||||
share.shareLink.isNotEmpty() ||
|
|
||||||
share.token.isEmpty()
|
|
||||||
) {
|
|
||||||
return@map share
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serverBaseUri != null) {
|
|
||||||
val sharingLinkPath = ShareUtils.SHARING_LINK_PATH
|
|
||||||
share.shareLink = serverBaseUri.toString() + sharingLinkPath + share.token
|
|
||||||
} else {
|
|
||||||
Timber.e("Couldn't build link for public share :(")
|
|
||||||
}
|
|
||||||
|
|
||||||
share
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resultData != null) {
|
|
||||||
result.setData(ShareParserResult(ArrayList(resultData.toMutableList())))
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
result = RemoteOperationResult(RemoteOperationResult.ResultCode.WRONG_SERVER_RESPONSE)
|
|
||||||
Timber.e("Successful status with no share in the response")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shareXmlParser?.isWrongParameter!! -> {
|
|
||||||
result = RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_WRONG_PARAMETER)
|
|
||||||
result.httpPhrase = shareXmlParser?.message
|
|
||||||
}
|
|
||||||
shareXmlParser?.isNotFound!! -> {
|
|
||||||
result = RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND)
|
|
||||||
result.httpPhrase = shareXmlParser?.message
|
|
||||||
}
|
|
||||||
shareXmlParser?.isForbidden!! -> {
|
|
||||||
result = RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_FORBIDDEN)
|
|
||||||
result.httpPhrase = shareXmlParser?.message
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
result = RemoteOperationResult(RemoteOperationResult.ResultCode.WRONG_SERVER_RESPONSE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: XmlPullParserException) {
|
|
||||||
Timber.e(e, "Error parsing response from server")
|
|
||||||
result = RemoteOperationResult(RemoteOperationResult.ResultCode.WRONG_SERVER_RESPONSE)
|
|
||||||
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Timber.e(e, "Error reading response from server")
|
|
||||||
result = RemoteOperationResult(RemoteOperationResult.ResultCode.WRONG_SERVER_RESPONSE)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,420 +0,0 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.owncloud.android.lib.resources.shares
|
|
||||||
|
|
||||||
import android.util.Xml
|
|
||||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
|
||||||
import org.xmlpull.v1.XmlPullParser
|
|
||||||
import org.xmlpull.v1.XmlPullParserException
|
|
||||||
import org.xmlpull.v1.XmlPullParserFactory
|
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.util.ArrayList
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parser for Share API Response
|
|
||||||
*
|
|
||||||
* @author masensio
|
|
||||||
* @author David González Verdugo
|
|
||||||
*/
|
|
||||||
|
|
||||||
class ShareXMLParser {
|
|
||||||
// Getters and Setters
|
|
||||||
var status: String? = null
|
|
||||||
var statusCode: Int = 0
|
|
||||||
var message: String? = null
|
|
||||||
|
|
||||||
val isSuccess: Boolean
|
|
||||||
get() = statusCode == SUCCESS
|
|
||||||
|
|
||||||
val isForbidden: Boolean
|
|
||||||
get() = statusCode == ERROR_FORBIDDEN
|
|
||||||
|
|
||||||
val isNotFound: Boolean
|
|
||||||
get() = statusCode == ERROR_NOT_FOUND
|
|
||||||
|
|
||||||
val isWrongParameter: Boolean
|
|
||||||
get() = statusCode == ERROR_WRONG_PARAMETER
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
init {
|
|
||||||
statusCode = INIT
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse is as response of Share API
|
|
||||||
* @param inputStream
|
|
||||||
* @return List of ShareRemoteFiles
|
|
||||||
* @throws XmlPullParserException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@Throws(XmlPullParserException::class, IOException::class)
|
|
||||||
fun parseXMLResponse(inputStream: InputStream): ArrayList<RemoteShare> {
|
|
||||||
|
|
||||||
try {
|
|
||||||
// XMLPullParser
|
|
||||||
val factory = XmlPullParserFactory.newInstance()
|
|
||||||
factory.isNamespaceAware = true
|
|
||||||
|
|
||||||
val parser = Xml.newPullParser()
|
|
||||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
|
|
||||||
parser.setInput(inputStream, null)
|
|
||||||
parser.nextTag()
|
|
||||||
return readOCS(parser)
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
inputStream.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse OCS node
|
|
||||||
* @param parser
|
|
||||||
* @return List of ShareRemoteFiles
|
|
||||||
* @throws XmlPullParserException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@Throws(XmlPullParserException::class, IOException::class)
|
|
||||||
private fun readOCS(parser: XmlPullParser): ArrayList<RemoteShare> {
|
|
||||||
var shares = ArrayList<RemoteShare>()
|
|
||||||
parser.require(XmlPullParser.START_TAG, ns, NODE_OCS)
|
|
||||||
while (parser.next() != XmlPullParser.END_TAG) {
|
|
||||||
if (parser.eventType != XmlPullParser.START_TAG) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val name = parser.name
|
|
||||||
// read NODE_META and NODE_DATA
|
|
||||||
when {
|
|
||||||
name.equals(NODE_META, ignoreCase = true) -> {
|
|
||||||
readMeta(parser)
|
|
||||||
}
|
|
||||||
name.equals(NODE_DATA, ignoreCase = true) -> {
|
|
||||||
shares = readData(parser)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
skip(parser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return shares
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse Meta node
|
|
||||||
* @param parser
|
|
||||||
* @throws XmlPullParserException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@Throws(XmlPullParserException::class, IOException::class)
|
|
||||||
private fun readMeta(parser: XmlPullParser) {
|
|
||||||
parser.require(XmlPullParser.START_TAG, ns, NODE_META)
|
|
||||||
while (parser.next() != XmlPullParser.END_TAG) {
|
|
||||||
if (parser.eventType != XmlPullParser.START_TAG) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val name = parser.name
|
|
||||||
|
|
||||||
when {
|
|
||||||
name.equals(NODE_STATUS, ignoreCase = true) -> {
|
|
||||||
status = readNode(parser, NODE_STATUS)
|
|
||||||
}
|
|
||||||
name.equals(NODE_STATUS_CODE, ignoreCase = true) -> {
|
|
||||||
statusCode = Integer.parseInt(readNode(parser, NODE_STATUS_CODE))
|
|
||||||
}
|
|
||||||
name.equals(NODE_MESSAGE, ignoreCase = true) -> {
|
|
||||||
message = readNode(parser, NODE_MESSAGE)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
skip(parser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse Data node
|
|
||||||
* @param parser
|
|
||||||
* @return
|
|
||||||
* @throws XmlPullParserException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@Throws(XmlPullParserException::class, IOException::class)
|
|
||||||
private fun readData(parser: XmlPullParser): ArrayList<RemoteShare> {
|
|
||||||
val shares = ArrayList<RemoteShare>()
|
|
||||||
var share: RemoteShare? = null
|
|
||||||
|
|
||||||
parser.require(XmlPullParser.START_TAG, ns, NODE_DATA)
|
|
||||||
while (parser.next() != XmlPullParser.END_TAG) {
|
|
||||||
if (parser.eventType != XmlPullParser.START_TAG) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val name = parser.name
|
|
||||||
when {
|
|
||||||
name.equals(NODE_ELEMENT, ignoreCase = true) -> {
|
|
||||||
readElement(parser, shares)
|
|
||||||
}
|
|
||||||
name.equals(NODE_ID, ignoreCase = true) -> {// Parse Create XML Response
|
|
||||||
share = RemoteShare()
|
|
||||||
val value = readNode(parser, NODE_ID)
|
|
||||||
share.id = value
|
|
||||||
}
|
|
||||||
name.equals(NODE_URL, ignoreCase = true) -> {
|
|
||||||
// NOTE: this field is received in all the public shares from OC 9.0.0
|
|
||||||
// in previous versions, it's received in the result of POST requests, but not
|
|
||||||
// in GET requests
|
|
||||||
share!!.shareType = ShareType.PUBLIC_LINK
|
|
||||||
val value = readNode(parser, NODE_URL)
|
|
||||||
share.shareLink = value
|
|
||||||
}
|
|
||||||
name.equals(NODE_TOKEN, ignoreCase = true) -> {
|
|
||||||
share!!.token = readNode(parser, NODE_TOKEN)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
skip(parser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (share != null) {
|
|
||||||
// this is the response of a request for creation; don't pass to isValidShare()
|
|
||||||
shares.add(share)
|
|
||||||
}
|
|
||||||
|
|
||||||
return shares
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse Element node
|
|
||||||
* @param parser
|
|
||||||
* @return
|
|
||||||
* @throws XmlPullParserException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@Throws(XmlPullParserException::class, IOException::class)
|
|
||||||
private fun readElement(parser: XmlPullParser, shares: ArrayList<RemoteShare>) {
|
|
||||||
parser.require(XmlPullParser.START_TAG, ns, NODE_ELEMENT)
|
|
||||||
|
|
||||||
val remoteShare = RemoteShare()
|
|
||||||
|
|
||||||
while (parser.next() != XmlPullParser.END_TAG) {
|
|
||||||
if (parser.eventType != XmlPullParser.START_TAG) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val name = parser.name
|
|
||||||
|
|
||||||
when {
|
|
||||||
name.equals(NODE_ELEMENT, ignoreCase = true) -> {
|
|
||||||
// patch to work around servers responding with extra <element> surrounding all
|
|
||||||
// the shares on the same file before
|
|
||||||
// https://github.com/owncloud/core/issues/6992 was fixed
|
|
||||||
readElement(parser, shares)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_ID, ignoreCase = true) -> {
|
|
||||||
remoteShare.id = readNode(parser, NODE_ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_ITEM_TYPE, ignoreCase = true) -> {
|
|
||||||
remoteShare.isFolder = readNode(parser, NODE_ITEM_TYPE).equals(TYPE_FOLDER, ignoreCase = true)
|
|
||||||
fixPathForFolder(remoteShare)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_PARENT, ignoreCase = true) -> {
|
|
||||||
readNode(parser, NODE_PARENT)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_SHARE_TYPE, ignoreCase = true) -> {
|
|
||||||
val value = Integer.parseInt(readNode(parser, NODE_SHARE_TYPE))
|
|
||||||
remoteShare.shareType = ShareType.fromValue(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_SHARE_WITH, ignoreCase = true) -> {
|
|
||||||
remoteShare.shareWith = readNode(parser, NODE_SHARE_WITH)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_PATH, ignoreCase = true) -> {
|
|
||||||
remoteShare.path = readNode(parser, NODE_PATH)
|
|
||||||
fixPathForFolder(remoteShare)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_PERMISSIONS, ignoreCase = true) -> {
|
|
||||||
remoteShare.permissions = Integer.parseInt(readNode(parser, NODE_PERMISSIONS))
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_STIME, ignoreCase = true) -> {
|
|
||||||
remoteShare.sharedDate = java.lang.Long.parseLong(readNode(parser, NODE_STIME))
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_EXPIRATION, ignoreCase = true) -> {
|
|
||||||
val value = readNode(parser, NODE_EXPIRATION)
|
|
||||||
if (value.isNotEmpty()) {
|
|
||||||
remoteShare.expirationDate = WebdavUtils.parseResponseDate(value)!!.time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_TOKEN, ignoreCase = true) -> {
|
|
||||||
remoteShare.token = readNode(parser, NODE_TOKEN)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_STORAGE, ignoreCase = true) -> {
|
|
||||||
readNode(parser, NODE_STORAGE)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_MAIL_SEND, ignoreCase = true) -> {
|
|
||||||
readNode(parser, NODE_MAIL_SEND)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_SHARE_WITH_DISPLAY_NAME, ignoreCase = true) -> {
|
|
||||||
remoteShare.sharedWithDisplayName = readNode(parser, NODE_SHARE_WITH_DISPLAY_NAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_SHARE_WITH_ADDITIONAL_INFO, ignoreCase = true) -> {
|
|
||||||
remoteShare.sharedWithAdditionalInfo = readNode(parser, NODE_SHARE_WITH_ADDITIONAL_INFO)
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_URL, ignoreCase = true) -> {
|
|
||||||
val value = readNode(parser, NODE_URL)
|
|
||||||
remoteShare.shareLink = value
|
|
||||||
}
|
|
||||||
|
|
||||||
name.equals(NODE_NAME, ignoreCase = true) -> {
|
|
||||||
remoteShare.name = readNode(parser, NODE_NAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
skip(parser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shares.add(remoteShare)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fixPathForFolder(share: RemoteShare) {
|
|
||||||
if (share.isFolder && share.path.isNotEmpty() &&
|
|
||||||
!share.path.endsWith(File.separator)
|
|
||||||
) {
|
|
||||||
share.path = share.path + File.separator
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a node, to obtain its text. Needs readText method
|
|
||||||
* @param parser
|
|
||||||
* @param node
|
|
||||||
* @return Text of the node
|
|
||||||
* @throws XmlPullParserException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@Throws(XmlPullParserException::class, IOException::class)
|
|
||||||
private fun readNode(parser: XmlPullParser, node: String): String {
|
|
||||||
parser.require(XmlPullParser.START_TAG, ns, node)
|
|
||||||
val value = readText(parser)
|
|
||||||
parser.require(XmlPullParser.END_TAG, ns, node)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the text from a node
|
|
||||||
* @param parser
|
|
||||||
* @return Text of the node
|
|
||||||
* @throws IOException
|
|
||||||
* @throws XmlPullParserException
|
|
||||||
*/
|
|
||||||
@Throws(IOException::class, XmlPullParserException::class)
|
|
||||||
private fun readText(parser: XmlPullParser): String {
|
|
||||||
var result = ""
|
|
||||||
if (parser.next() == XmlPullParser.TEXT) {
|
|
||||||
result = parser.text
|
|
||||||
parser.nextTag()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skip tags in parser procedure
|
|
||||||
* @param parser
|
|
||||||
* @throws XmlPullParserException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@Throws(XmlPullParserException::class, IOException::class)
|
|
||||||
private fun skip(parser: XmlPullParser) {
|
|
||||||
if (parser.eventType != XmlPullParser.START_TAG) {
|
|
||||||
throw IllegalStateException()
|
|
||||||
}
|
|
||||||
var depth = 1
|
|
||||||
while (depth != 0) {
|
|
||||||
when (parser.next()) {
|
|
||||||
XmlPullParser.END_TAG -> depth--
|
|
||||||
XmlPullParser.START_TAG -> depth++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
// No namespaces
|
|
||||||
private val ns: String? = null
|
|
||||||
|
|
||||||
// NODES for XML Parser
|
|
||||||
private const val NODE_OCS = "ocs"
|
|
||||||
|
|
||||||
private const val NODE_META = "meta"
|
|
||||||
private const val NODE_STATUS = "status"
|
|
||||||
private const val NODE_STATUS_CODE = "statuscode"
|
|
||||||
private const val NODE_MESSAGE = "message"
|
|
||||||
|
|
||||||
private const val NODE_DATA = "data"
|
|
||||||
private const val NODE_ELEMENT = "element"
|
|
||||||
private const val NODE_ID = "id"
|
|
||||||
private const val NODE_ITEM_TYPE = "item_type"
|
|
||||||
private const val NODE_PARENT = "parent"
|
|
||||||
private const val NODE_SHARE_TYPE = "share_type"
|
|
||||||
private const val NODE_SHARE_WITH = "share_with"
|
|
||||||
private const val NODE_PATH = "path"
|
|
||||||
private const val NODE_PERMISSIONS = "permissions"
|
|
||||||
private const val NODE_STIME = "stime"
|
|
||||||
private const val NODE_EXPIRATION = "expiration"
|
|
||||||
private const val NODE_TOKEN = "token"
|
|
||||||
private const val NODE_STORAGE = "storage"
|
|
||||||
private const val NODE_MAIL_SEND = "mail_send"
|
|
||||||
private const val NODE_SHARE_WITH_DISPLAY_NAME = "share_with_displayname"
|
|
||||||
private const val NODE_SHARE_WITH_ADDITIONAL_INFO = "share_with_additional_info"
|
|
||||||
private const val NODE_NAME = "name"
|
|
||||||
|
|
||||||
private const val NODE_URL = "url"
|
|
||||||
|
|
||||||
private const val TYPE_FOLDER = "folder"
|
|
||||||
|
|
||||||
private const val SUCCESS = 200
|
|
||||||
private const val ERROR_WRONG_PARAMETER = 400
|
|
||||||
private const val ERROR_FORBIDDEN = 403
|
|
||||||
private const val ERROR_NOT_FOUND = 404
|
|
||||||
private const val INIT = -1
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,9 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
/* ownCloud Android Library is available under MIT license
|
||||||
*
|
* @author masensio
|
||||||
* Copyright (C) 2020 ownCloud GmbH.
|
* @author David A. Velasco
|
||||||
|
* @author David González Verdugo
|
||||||
|
* @author Fernando Sanz Velasco
|
||||||
|
* Copyright (C) 2021 ownCloud GmbH
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -25,14 +28,23 @@
|
|||||||
|
|
||||||
package com.owncloud.android.lib.resources.shares
|
package com.owncloud.android.lib.resources.shares
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.PARAM_FORMAT
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.VALUE_FORMAT
|
||||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.PutMethod
|
import com.owncloud.android.lib.common.http.methods.nonwebdav.PutMethod
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.CommonOcsResponse
|
||||||
import com.owncloud.android.lib.resources.shares.RemoteShare.Companion.DEFAULT_PERMISSION
|
import com.owncloud.android.lib.resources.shares.RemoteShare.Companion.DEFAULT_PERMISSION
|
||||||
|
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 okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.lang.reflect.Type
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
@ -46,6 +58,7 @@ import java.util.Locale
|
|||||||
*
|
*
|
||||||
* @author David A. Velasco
|
* @author David A. Velasco
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
|
* @author Fernando Sanz Velasco
|
||||||
*/
|
*/
|
||||||
class UpdateRemoteShareOperation
|
class UpdateRemoteShareOperation
|
||||||
/**
|
/**
|
||||||
@ -57,7 +70,7 @@ class UpdateRemoteShareOperation
|
|||||||
*/
|
*/
|
||||||
private val remoteId: String
|
private val remoteId: String
|
||||||
|
|
||||||
) : RemoteOperation<ShareParserResult>() {
|
) : RemoteOperation<ShareResponse>() {
|
||||||
/**
|
/**
|
||||||
* Name to update in Share resource. Ignored by servers previous to version 10.0.0
|
* Name to update in Share resource. Ignored by servers previous to version 10.0.0
|
||||||
*
|
*
|
||||||
@ -90,109 +103,128 @@ class UpdateRemoteShareOperation
|
|||||||
*/
|
*/
|
||||||
var permissions: Int = DEFAULT_PERMISSION
|
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
|
var retrieveShareDetails = false // To retrieve more info about the just updated share
|
||||||
|
|
||||||
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareParserResult> {
|
private fun buildRequestUri(baseUri: Uri) =
|
||||||
var result: RemoteOperationResult<ShareParserResult>
|
baseUri.buildUpon()
|
||||||
|
.appendEncodedPath(OCS_ROUTE)
|
||||||
|
.appendEncodedPath(remoteId)
|
||||||
|
.appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT)
|
||||||
|
.build()
|
||||||
|
|
||||||
try {
|
private fun parseResponse(response: String): ShareResponse {
|
||||||
val formBodyBuilder = FormBody.Builder()
|
val moshi = Moshi.Builder().build()
|
||||||
|
val commonOcsType: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareItem::class.java)
|
||||||
|
val adapter: JsonAdapter<CommonOcsResponse<ShareItem>> = moshi.adapter(commonOcsType)
|
||||||
|
val remoteShare = adapter.fromJson(response)?.ocs?.data?.toRemoteShare()
|
||||||
|
return ShareResponse(remoteShare?.let { listOf(it) } ?: listOf())
|
||||||
|
}
|
||||||
|
|
||||||
// Parameters to update
|
private fun onResultUnsuccessful(
|
||||||
if (name != null) {
|
method: PutMethod,
|
||||||
formBodyBuilder.add(PARAM_NAME, name!!)
|
response: String?,
|
||||||
}
|
status: Int
|
||||||
|
): RemoteOperationResult<ShareResponse> {
|
||||||
|
Timber.e("Failed response while while updating remote shares ")
|
||||||
|
if (response != null) {
|
||||||
|
Timber.e("*** status code: $status; response message: $response")
|
||||||
|
} else {
|
||||||
|
Timber.e("*** status code: $status")
|
||||||
|
}
|
||||||
|
return RemoteOperationResult(method)
|
||||||
|
}
|
||||||
|
|
||||||
if (password != null) {
|
private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareResponse> {
|
||||||
formBodyBuilder.add(PARAM_PASSWORD, password!!)
|
val result = RemoteOperationResult<ShareResponse>(RemoteOperationResult.ResultCode.OK)
|
||||||
}
|
Timber.d("Successful response: $response")
|
||||||
|
result.data = parseResponse(response!!)
|
||||||
|
Timber.d("*** Retrieve the index of the new share completed ")
|
||||||
|
val emptyShare = result.data.shares.first()
|
||||||
|
|
||||||
if (expirationDateInMillis < INITIAL_EXPIRATION_DATE_IN_MILLIS) {
|
return if (retrieveShareDetails) {
|
||||||
// clear expiration date
|
// retrieve more info - PUT only returns the index of the new share
|
||||||
formBodyBuilder.add(PARAM_EXPIRATION_DATE, "")
|
GetRemoteShareOperation(emptyShare.id).execute(client)
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if (expirationDateInMillis > INITIAL_EXPIRATION_DATE_IN_MILLIS) {
|
private fun createFormBodyBuilder(): FormBody.Builder {
|
||||||
// set expiration date
|
val formBodyBuilder = FormBody.Builder()
|
||||||
val dateFormat = SimpleDateFormat(FORMAT_EXPIRATION_DATE, Locale.getDefault())
|
|
||||||
val expirationDate = Calendar.getInstance()
|
|
||||||
expirationDate.timeInMillis = expirationDateInMillis
|
|
||||||
val formattedExpirationDate = dateFormat.format(expirationDate.time)
|
|
||||||
formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate)
|
|
||||||
} // else, ignore - no update
|
|
||||||
|
|
||||||
if (publicUpload != null) {
|
// Parameters to update
|
||||||
formBodyBuilder.add(PARAM_PUBLIC_UPLOAD, publicUpload.toString())
|
if (name != null) {
|
||||||
}
|
formBodyBuilder.add(PARAM_NAME, name.orEmpty())
|
||||||
|
|
||||||
// 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) {
|
|
||||||
// set permissions
|
|
||||||
formBodyBuilder.add(PARAM_PERMISSIONS, permissions.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
val requestUri = client.baseUri
|
|
||||||
val uriBuilder = requestUri.buildUpon()
|
|
||||||
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH)
|
|
||||||
uriBuilder.appendEncodedPath(remoteId.toString())
|
|
||||||
|
|
||||||
val putMethod = PutMethod(URL(uriBuilder.build().toString()), formBodyBuilder.build())
|
|
||||||
|
|
||||||
putMethod.setRequestHeader(HttpConstants.CONTENT_TYPE_HEADER, HttpConstants.CONTENT_TYPE_URLENCODED_UTF8)
|
|
||||||
putMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
|
||||||
|
|
||||||
val status = client.executeHttpMethod(putMethod)
|
|
||||||
|
|
||||||
// Parse xml response
|
|
||||||
val parser = ShareToRemoteOperationResultParser(
|
|
||||||
ShareXMLParser()
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!isSuccess(status)) {
|
|
||||||
return parser.parse(putMethod.getResponseBodyAsString())
|
|
||||||
}
|
|
||||||
|
|
||||||
parser.ownCloudVersion = client.ownCloudVersion
|
|
||||||
parser.serverBaseUri = client.baseUri
|
|
||||||
result = parser.parse(putMethod.getResponseBodyAsString())
|
|
||||||
|
|
||||||
if (result.isSuccess && retrieveShareDetails) {
|
|
||||||
// retrieve more info - PUT only returns the index of the new share
|
|
||||||
val emptyShare = result.data.shares.first()
|
|
||||||
val getInfo = GetRemoteShareOperation(
|
|
||||||
emptyShare.id
|
|
||||||
)
|
|
||||||
result = getInfo.execute(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e: Exception) {
|
|
||||||
result = RemoteOperationResult(e)
|
|
||||||
Timber.e(e, "Exception while Creating New Share")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
if (password != null) {
|
||||||
|
formBodyBuilder.add(PARAM_PASSWORD, password.orEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expirationDateInMillis < INITIAL_EXPIRATION_DATE_IN_MILLIS) {
|
||||||
|
// clear expiration date
|
||||||
|
formBodyBuilder.add(PARAM_EXPIRATION_DATE, "")
|
||||||
|
|
||||||
|
} else if (expirationDateInMillis > INITIAL_EXPIRATION_DATE_IN_MILLIS) {
|
||||||
|
// set expiration date
|
||||||
|
val dateFormat = SimpleDateFormat(FORMAT_EXPIRATION_DATE, Locale.getDefault())
|
||||||
|
val expirationDate = Calendar.getInstance()
|
||||||
|
expirationDate.timeInMillis = expirationDateInMillis
|
||||||
|
val formattedExpirationDate = dateFormat.format(expirationDate.time)
|
||||||
|
formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate)
|
||||||
|
} // else, ignore - no update
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// set permissions
|
||||||
|
formBodyBuilder.add(PARAM_PERMISSIONS, permissions.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return formBodyBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<ShareResponse> {
|
||||||
|
val requestUri = buildRequestUri(client.baseUri)
|
||||||
|
|
||||||
|
val formBodyBuilder = createFormBodyBuilder()
|
||||||
|
|
||||||
|
val putMethod = PutMethod(URL(requestUri.toString()), formBodyBuilder.build()).apply {
|
||||||
|
setRequestHeader(HttpConstants.CONTENT_TYPE_HEADER, HttpConstants.CONTENT_TYPE_URLENCODED_UTF8)
|
||||||
|
addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val status = client.executeHttpMethod(putMethod)
|
||||||
|
val response = putMethod.getResponseBodyAsString()
|
||||||
|
|
||||||
|
if (isSuccess(status)) {
|
||||||
|
onRequestSuccessful(response)
|
||||||
|
} else {
|
||||||
|
onResultUnsuccessful(putMethod, response, status)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Exception while updating remote share")
|
||||||
|
RemoteOperationResult(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
|
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
//OCS Route
|
||||||
|
private const val OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/shares"
|
||||||
|
|
||||||
|
//Arguments - names
|
||||||
private const val PARAM_NAME = "name"
|
private const val PARAM_NAME = "name"
|
||||||
private const val PARAM_PASSWORD = "password"
|
private const val PARAM_PASSWORD = "password"
|
||||||
private const val PARAM_EXPIRATION_DATE = "expireDate"
|
private const val PARAM_EXPIRATION_DATE = "expireDate"
|
||||||
private const val PARAM_PERMISSIONS = "permissions"
|
private const val PARAM_PERMISSIONS = "permissions"
|
||||||
private const val PARAM_PUBLIC_UPLOAD = "publicUpload"
|
|
||||||
private const val FORMAT_EXPIRATION_DATE = "yyyy-MM-dd"
|
|
||||||
private const val ENTITY_CONTENT_TYPE = "application/x-www-form-urlencoded"
|
|
||||||
private const val ENTITY_CHARSET = "UTF-8"
|
|
||||||
|
|
||||||
|
//Arguments - constant values
|
||||||
|
private const val FORMAT_EXPIRATION_DATE = "yyyy-MM-dd"
|
||||||
private const val INITIAL_EXPIRATION_DATE_IN_MILLIS: Long = 0
|
private const val INITIAL_EXPIRATION_DATE_IN_MILLIS: Long = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user