mirror of
https://github.com/owncloud/android-library.git
synced 2025-06-08 00:16:09 +00:00
commit
a0a3f92576
17
.github/dependabot.yml
vendored
Normal file
17
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "gradle"
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
labels:
|
||||||
|
- "dependencies"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
@ -1,7 +1,7 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
kotlinVersion = '1.4.20'
|
kotlinVersion = '1.4.31'
|
||||||
moshiVersion = "1.9.2"
|
moshiVersion = "1.11.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@ -9,7 +9,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
classpath 'com.android.tools.build:gradle:4.1.2'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,5 @@
|
|||||||
#Sun Sep 08 09:13:16 CEST 2019
|
|
||||||
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
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
|
||||||
|
149
gradlew
vendored
149
gradlew
vendored
@ -1,4 +1,20 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
@ -6,47 +22,6 @@
|
|||||||
##
|
##
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
DEFAULT_JVM_OPTS=""
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=`basename "$0"`
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
|
||||||
MAX_FD="maximum"
|
|
||||||
|
|
||||||
warn ( ) {
|
|
||||||
echo "$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
die ( ) {
|
|
||||||
echo
|
|
||||||
echo "$*"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
|
||||||
cygwin=false
|
|
||||||
msys=false
|
|
||||||
darwin=false
|
|
||||||
case "`uname`" in
|
|
||||||
CYGWIN* )
|
|
||||||
cygwin=true
|
|
||||||
;;
|
|
||||||
Darwin* )
|
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
|
||||||
if $cygwin ; then
|
|
||||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 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"
|
PRG="$0"
|
||||||
@ -61,12 +36,53 @@ while [ -h "$PRG" ] ; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
SAVED="`pwd`"
|
||||||
cd "`dirname \"$PRG\"`/" >&-
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
APP_HOME="`pwd -P`"
|
APP_HOME="`pwd -P`"
|
||||||
cd "$SAVED" >&-
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# 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"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
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
|
||||||
@ -90,7 +106,7 @@ 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" ] ; then
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
if [ $? -eq 0 ] ; then
|
if [ $? -eq 0 ] ; then
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
@ -110,11 +126,13 @@ if $darwin; then
|
|||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if $cygwin ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
SEP=""
|
SEP=""
|
||||||
@ -138,27 +156,30 @@ if $cygwin ; then
|
|||||||
else
|
else
|
||||||
eval `echo args$i`="\"$arg\""
|
eval `echo args$i`="\"$arg\""
|
||||||
fi
|
fi
|
||||||
i=$((i+1))
|
i=`expr $i + 1`
|
||||||
done
|
done
|
||||||
case $i in
|
case $i in
|
||||||
(0) set -- ;;
|
0) set -- ;;
|
||||||
(1) set -- "$args0" ;;
|
1) set -- "$args0" ;;
|
||||||
(2) set -- "$args0" "$args1" ;;
|
2) set -- "$args0" "$args1" ;;
|
||||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
# Escape application args
|
||||||
function splitJvmOpts() {
|
save () {
|
||||||
JVM_OPTS=("$@")
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
}
|
}
|
||||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
APP_ARGS=`save "$@"`
|
||||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
|
||||||
|
|
||||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
|
53
gradlew.bat
vendored
53
gradlew.bat
vendored
@ -1,3 +1,19 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@ -8,20 +24,23 @@
|
|||||||
@rem Set local scope for the variables with windows NT shell
|
@rem Set local scope for the variables with windows NT shell
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
set DEFAULT_JVM_OPTS=
|
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@ -35,7 +54,7 @@ goto fail
|
|||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
@ -45,34 +64,14 @@ echo location of your Java installation.
|
|||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:init
|
|
||||||
@rem Get command-line arguments, handling Windowz variants
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
|
||||||
|
|
||||||
:win9xME_args
|
|
||||||
@rem Slurp the command line arguments.
|
|
||||||
set CMD_LINE_ARGS=
|
|
||||||
set _SKIP=2
|
|
||||||
|
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
|
||||||
goto execute
|
|
||||||
|
|
||||||
:4NT_args
|
|
||||||
@rem Get arguments from the 4NT Shell from JP Software
|
|
||||||
set CMD_LINE_ARGS=%$
|
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
@ -9,12 +9,13 @@ dependencies {
|
|||||||
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:$moshiVersion") {
|
||||||
exclude module: "kotlin-reflect"
|
exclude module: "kotlin-reflect"
|
||||||
}
|
}
|
||||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
|
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13.1'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
|
testImplementation 'org.robolectric:robolectric:4.3.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -24,8 +25,8 @@ android {
|
|||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
|
|
||||||
versionCode = 10000900
|
versionCode = 10001000
|
||||||
versionName = "1.0.9"
|
versionName = "1.0.10"
|
||||||
}
|
}
|
||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
@ -37,4 +38,10 @@ android {
|
|||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests {
|
||||||
|
includeAndroidResources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,15 +48,15 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER;
|
||||||
import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID;
|
import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID;
|
||||||
|
|
||||||
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 WEBDAV_PATH_4_0_AND_LATER = "/remote.php/dav";
|
||||||
private static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/";
|
|
||||||
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 int MAX_REDIRECTIONS_COUNT = 3;
|
private static final int MAX_REDIRECTIONS_COUNT = 3;
|
||||||
private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1;
|
private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1;
|
||||||
|
|
||||||
@ -104,8 +104,8 @@ public class OwnCloudClient extends HttpClient {
|
|||||||
method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId);
|
method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId);
|
||||||
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
|
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
|
||||||
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
|
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
|
||||||
if (mCredentials.getHeaderAuth() != null) {
|
if (mCredentials.getHeaderAuth() != null && method.getRequestHeader(AUTHORIZATION_HEADER) == null) {
|
||||||
method.setRequestHeader(HttpConstants.AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
|
method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
|
||||||
}
|
}
|
||||||
status = method.execute();
|
status = method.execute();
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ public class OwnCloudClient extends HttpClient {
|
|||||||
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
|
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
|
||||||
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
|
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
|
||||||
if (mCredentials.getHeaderAuth() != null) {
|
if (mCredentials.getHeaderAuth() != null) {
|
||||||
method.setRequestHeader(HttpConstants.AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
|
method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
|
||||||
}
|
}
|
||||||
status = method.execute();
|
status = method.execute();
|
||||||
|
|
||||||
|
@ -27,6 +27,9 @@ package com.owncloud.android.lib.common;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.http.HttpClient;
|
||||||
|
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation;
|
||||||
|
|
||||||
public class OwnCloudClientFactory {
|
public class OwnCloudClientFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,8 +45,14 @@ public class OwnCloudClientFactory {
|
|||||||
|
|
||||||
client.setFollowRedirects(followRedirects);
|
client.setFollowRedirects(followRedirects);
|
||||||
|
|
||||||
client.setContext(context);
|
HttpClient.setContext(context);
|
||||||
|
retrieveCookiesFromMiddleware(client);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void retrieveCookiesFromMiddleware(OwnCloudClient client) {
|
||||||
|
final GetRemoteStatusOperation statusOperation = new GetRemoteStatusOperation();
|
||||||
|
statusOperation.run(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,6 @@
|
|||||||
|
|
||||||
package com.owncloud.android.lib.common;
|
package com.owncloud.android.lib.common;
|
||||||
|
|
||||||
import android.accounts.Account;
|
|
||||||
import android.accounts.AccountManager;
|
|
||||||
import android.accounts.AuthenticatorException;
|
import android.accounts.AuthenticatorException;
|
||||||
import android.accounts.OperationCanceledException;
|
import android.accounts.OperationCanceledException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -37,7 +35,6 @@ import com.owncloud.android.lib.common.http.HttpClient;
|
|||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
@ -111,6 +108,12 @@ public class SingleSessionManager {
|
|||||||
account.getBaseUri(),
|
account.getBaseUri(),
|
||||||
context.getApplicationContext(),
|
context.getApplicationContext(),
|
||||||
true); // TODO remove dependency on OwnCloudClientFactory
|
true); // TODO remove dependency on OwnCloudClientFactory
|
||||||
|
|
||||||
|
//the next two lines are a hack because okHttpclient is used as a singleton instead of being an
|
||||||
|
//injected instance that can be deleted when required
|
||||||
|
client.clearCookies();
|
||||||
|
client.clearCredentials();
|
||||||
|
|
||||||
client.setAccount(account);
|
client.setAccount(account);
|
||||||
HttpClient.setContext(context);
|
HttpClient.setContext(context);
|
||||||
|
|
||||||
@ -130,7 +133,6 @@ public class SingleSessionManager {
|
|||||||
Timber.v("reusing client for session %s", sessionName);
|
Timber.v("reusing client for session %s", sessionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
keepCookiesUpdated(context, account, client);
|
|
||||||
keepUriUpdated(account, client);
|
keepUriUpdated(account, client);
|
||||||
}
|
}
|
||||||
Timber.d("getClientFor finishing ");
|
Timber.d("getClientFor finishing ");
|
||||||
@ -161,32 +163,6 @@ public class SingleSessionManager {
|
|||||||
Timber.d("removeClientFor finishing ");
|
Timber.d("removeClientFor finishing ");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveAllClients(Context context, String accountType) {
|
|
||||||
Timber.d("Saving sessions... ");
|
|
||||||
|
|
||||||
Iterator<String> accountNames = mClientsWithKnownUsername.keySet().iterator();
|
|
||||||
String accountName;
|
|
||||||
Account account;
|
|
||||||
while (accountNames.hasNext()) {
|
|
||||||
accountName = accountNames.next();
|
|
||||||
account = new Account(accountName, accountType);
|
|
||||||
AccountUtils.saveClient(mClientsWithKnownUsername.get(accountName), account, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("All sessions saved");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void keepCookiesUpdated(Context context, OwnCloudAccount account, OwnCloudClient reusedClient) {
|
|
||||||
AccountManager am = AccountManager.get(context.getApplicationContext());
|
|
||||||
if (am != null && account.getSavedAccount() != null) {
|
|
||||||
String recentCookies = am.getUserData(account.getSavedAccount(), AccountUtils.Constants.KEY_COOKIES);
|
|
||||||
String previousCookies = reusedClient.getCookiesString();
|
|
||||||
if (recentCookies != null && !previousCookies.equals("") && !recentCookies.equals(previousCookies)) {
|
|
||||||
AccountUtils.restoreCookies(account.getSavedAccount(), reusedClient, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refreshCredentialsForAccount(String accountName, OwnCloudCredentials credentials) {
|
public void refreshCredentialsForAccount(String accountName, OwnCloudCredentials credentials) {
|
||||||
OwnCloudClient ownCloudClient = mClientsWithKnownUsername.get(accountName);
|
OwnCloudClient ownCloudClient = mClientsWithKnownUsername.get(accountName);
|
||||||
if (ownCloudClient == null) {
|
if (ownCloudClient == null) {
|
||||||
|
@ -36,15 +36,10 @@ import android.net.Uri;
|
|||||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||||
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;
|
||||||
import com.owncloud.android.lib.resources.files.FileUtils;
|
|
||||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
|
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
|
||||||
import okhttp3.Cookie;
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AccountUtils {
|
public class AccountUtils {
|
||||||
/**
|
/**
|
||||||
@ -202,64 +197,6 @@ public class AccountUtils {
|
|||||||
return username + "@" + url;
|
return username + "@" + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveClient(OwnCloudClient client, Account savedAccount, Context context) {
|
|
||||||
// Account Manager
|
|
||||||
AccountManager ac = AccountManager.get(context.getApplicationContext());
|
|
||||||
|
|
||||||
if (client != null) {
|
|
||||||
String cookiesString = client.getCookiesString();
|
|
||||||
if (!"".equals(cookiesString)) {
|
|
||||||
ac.setUserData(savedAccount, Constants.KEY_COOKIES, cookiesString);
|
|
||||||
Timber.d("Saving Cookies: %s", cookiesString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore the client cookies persisted in an account stored in the system AccountManager.
|
|
||||||
*
|
|
||||||
* @param account Stored account.
|
|
||||||
* @param client Client to restore cookies in.
|
|
||||||
* @param context Android context used to access the system AccountManager.
|
|
||||||
*/
|
|
||||||
public static void restoreCookies(Account account, OwnCloudClient client, Context context) {
|
|
||||||
if (account == null) {
|
|
||||||
Timber.d("Cannot restore cookie for null account");
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Timber.d("Restoring cookies for %s", account.name);
|
|
||||||
|
|
||||||
// Account Manager
|
|
||||||
AccountManager am = AccountManager.get(context.getApplicationContext());
|
|
||||||
|
|
||||||
Uri serverUri = (client.getBaseUri() != null) ? client.getBaseUri() : client.getUserFilesWebDavUri();
|
|
||||||
|
|
||||||
String cookiesString = am.getUserData(account, Constants.KEY_COOKIES);
|
|
||||||
if (cookiesString != null) {
|
|
||||||
String[] rawCookies = cookiesString.split(";");
|
|
||||||
List<Cookie> cookieList = new ArrayList<>(rawCookies.length);
|
|
||||||
for (String rawCookie : rawCookies) {
|
|
||||||
rawCookie = rawCookie.replace(" ", "");
|
|
||||||
final int equalPos = rawCookie.indexOf('=');
|
|
||||||
if (equalPos == -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
cookieList.add(new Cookie.Builder()
|
|
||||||
.name(rawCookie.substring(0, equalPos))
|
|
||||||
.value(rawCookie.substring(equalPos + 1))
|
|
||||||
.domain(serverUri.getHost())
|
|
||||||
.path(
|
|
||||||
serverUri.getPath().equals("")
|
|
||||||
? File.separator
|
|
||||||
: serverUri.getPath()
|
|
||||||
)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
client.setCookiesForCurrentAccount(cookieList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AccountNotFoundException extends AccountsException {
|
public static class AccountNotFoundException extends AccountsException {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -299,11 +236,6 @@ public class AccountUtils {
|
|||||||
|
|
||||||
public static final String OAUTH_SUPPORTED_TRUE = "TRUE";
|
public static final String OAUTH_SUPPORTED_TRUE = "TRUE";
|
||||||
|
|
||||||
/**
|
|
||||||
* OC account cookies
|
|
||||||
*/
|
|
||||||
public static final String KEY_COOKIES = "oc_account_cookies";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OC account version
|
* OC account version
|
||||||
*/
|
*/
|
||||||
|
@ -24,10 +24,6 @@
|
|||||||
|
|
||||||
package com.owncloud.android.lib.common.authentication;
|
package com.owncloud.android.lib.common.authentication;
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
|
||||||
import com.owncloud.android.lib.common.http.HttpClient;
|
|
||||||
import com.owncloud.android.lib.common.http.HttpConstants;
|
|
||||||
|
|
||||||
public class OwnCloudCredentialsFactory {
|
public class OwnCloudCredentialsFactory {
|
||||||
|
|
||||||
public static final String CREDENTIAL_CHARSET = "UTF-8";
|
public static final String CREDENTIAL_CHARSET = "UTF-8";
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
/* 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.common.http
|
||||||
|
|
||||||
|
import okhttp3.Cookie
|
||||||
|
import okhttp3.CookieJar
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
|
||||||
|
class CookieJarImpl(
|
||||||
|
private val sCookieStore: HashMap<String, List<Cookie>>
|
||||||
|
) : CookieJar {
|
||||||
|
|
||||||
|
fun containsCookieWithName(cookies: List<Cookie>, name: String): Boolean {
|
||||||
|
for (cookie: Cookie in cookies) {
|
||||||
|
if (cookie.name == name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUpdatedCookies(oldCookies: List<Cookie>, newCookies: List<Cookie>): List<Cookie> {
|
||||||
|
val updatedList = ArrayList<Cookie>(newCookies)
|
||||||
|
for (oldCookie: Cookie in oldCookies) {
|
||||||
|
if (!containsCookieWithName(updatedList, oldCookie.name)) {
|
||||||
|
updatedList.add(oldCookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updatedList
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||||
|
// Avoid duplicated cookies but update
|
||||||
|
val currentCookies: List<Cookie> = sCookieStore[url.host] ?: ArrayList()
|
||||||
|
val updatedCookies: List<Cookie> = getUpdatedCookies(currentCookies, cookies)
|
||||||
|
sCookieStore[url.host] = updatedCookies
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadForRequest(url: HttpUrl) =
|
||||||
|
sCookieStore[url.host] ?: ArrayList()
|
||||||
|
|
||||||
|
}
|
@ -33,19 +33,18 @@ import okhttp3.CookieJar;
|
|||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Protocol;
|
import okhttp3.Protocol;
|
||||||
|
import okhttp3.TlsVersion;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
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.ArrayList;
|
import java.util.Collections;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,6 +52,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*
|
*
|
||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class HttpClient {
|
public class HttpClient {
|
||||||
private static OkHttpClient sOkHttpClient;
|
private static OkHttpClient sOkHttpClient;
|
||||||
private static Context sContext;
|
private static Context sContext;
|
||||||
@ -64,66 +64,13 @@ public class HttpClient {
|
|||||||
try {
|
try {
|
||||||
final X509TrustManager trustManager = new AdvancedX509TrustManager(
|
final X509TrustManager trustManager = new AdvancedX509TrustManager(
|
||||||
NetworkUtils.getKnownServersStore(sContext));
|
NetworkUtils.getKnownServersStore(sContext));
|
||||||
|
final SSLSocketFactory sslSocketFactory = getNewSslSocketFactory(trustManager);
|
||||||
SSLContext sslContext;
|
|
||||||
|
|
||||||
try {
|
|
||||||
sslContext = SSLContext.getInstance("TLSv1.3");
|
|
||||||
} catch (NoSuchAlgorithmException tlsv13Exception) {
|
|
||||||
try {
|
|
||||||
Timber.w("TLSv1.3 is not supported in this device; falling through TLSv1.2");
|
|
||||||
sslContext = SSLContext.getInstance("TLSv1.2");
|
|
||||||
} catch (NoSuchAlgorithmException tlsv12Exception) {
|
|
||||||
try {
|
|
||||||
Timber.w("TLSv1.2 is not supported in this device; falling through TLSv1.1");
|
|
||||||
sslContext = SSLContext.getInstance("TLSv1.1");
|
|
||||||
} catch (NoSuchAlgorithmException tlsv11Exception) {
|
|
||||||
Timber.w("TLSv1.1 is not supported in this device; falling through TLSv1.0");
|
|
||||||
sslContext = SSLContext.getInstance("TLSv1");
|
|
||||||
// should be available in any device; see reference of supported protocols in
|
|
||||||
// http://developer.android.com/reference/javax/net/ssl/SSLSocket.html
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sslContext.init(null, new TrustManager[]{trustManager}, null);
|
|
||||||
|
|
||||||
SSLSocketFactory sslSocketFactory;
|
|
||||||
|
|
||||||
sslSocketFactory = sslContext.getSocketFactory();
|
|
||||||
|
|
||||||
// Automatic cookie handling, NOT PERSISTENT
|
// Automatic cookie handling, NOT PERSISTENT
|
||||||
CookieJar cookieJar = new CookieJar() {
|
final CookieJar cookieJar = new CookieJarImpl(sCookieStore);
|
||||||
@Override
|
|
||||||
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
|
|
||||||
// Avoid duplicated cookies
|
|
||||||
Set<Cookie> nonDuplicatedCookiesSet = new HashSet<>(cookies);
|
|
||||||
List<Cookie> nonDuplicatedCookiesList = new ArrayList<>(nonDuplicatedCookiesSet);
|
|
||||||
|
|
||||||
sCookieStore.put(url.host(), nonDuplicatedCookiesList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Cookie> loadForRequest(HttpUrl url) {
|
|
||||||
List<Cookie> cookies = sCookieStore.get(url.host());
|
|
||||||
return cookies != null ? cookies : new ArrayList<>();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
|
|
||||||
.addNetworkInterceptor(getLogInterceptor())
|
|
||||||
.protocols(Arrays.asList(Protocol.HTTP_1_1))
|
|
||||||
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
|
||||||
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
|
||||||
.connectTimeout(HttpConstants.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
|
|
||||||
.followRedirects(false)
|
|
||||||
.sslSocketFactory(sslSocketFactory, trustManager)
|
|
||||||
.hostnameVerifier((asdf, usdf) -> true)
|
|
||||||
.cookieJar(cookieJar);
|
|
||||||
// TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok.
|
// TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok.
|
||||||
//.hostnameVerifier(new BrowserCompatHostnameVerifier());
|
//.hostnameVerifier(new BrowserCompatHostnameVerifier());
|
||||||
|
sOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar);
|
||||||
sOkHttpClient = clientBuilder.build();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Timber.e(e, "Could not setup SSL system.");
|
Timber.e(e, "Could not setup SSL system.");
|
||||||
@ -132,6 +79,60 @@ public class HttpClient {
|
|||||||
return sOkHttpClient;
|
return sOkHttpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static SSLContext getSslContext() throws NoSuchAlgorithmException {
|
||||||
|
try {
|
||||||
|
return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName());
|
||||||
|
} catch (NoSuchAlgorithmException tlsv13Exception) {
|
||||||
|
try {
|
||||||
|
Timber.w("TLSv1.3 is not supported in this device; falling through TLSv1.2");
|
||||||
|
return SSLContext.getInstance(TlsVersion.TLS_1_2.javaName());
|
||||||
|
} catch (NoSuchAlgorithmException tlsv12Exception) {
|
||||||
|
try {
|
||||||
|
Timber.w("TLSv1.2 is not supported in this device; falling through TLSv1.1");
|
||||||
|
return SSLContext.getInstance(TlsVersion.TLS_1_1.javaName());
|
||||||
|
} catch (NoSuchAlgorithmException tlsv11Exception) {
|
||||||
|
Timber.w("TLSv1.1 is not supported in this device; falling through TLSv1.0");
|
||||||
|
return SSLContext.getInstance(TlsVersion.TLS_1_0.javaName());
|
||||||
|
// should be available in any device; see reference of supported protocols in
|
||||||
|
// http://developer.android.com/reference/javax/net/ssl/SSLSocket.html
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SSLSocketFactory getNewSslSocketFactory(X509TrustManager trustManager)
|
||||||
|
throws NoSuchAlgorithmException, KeyManagementException {
|
||||||
|
final SSLContext sslContext = getSslContext();
|
||||||
|
sslContext.init(null, new TrustManager[]{trustManager}, null);
|
||||||
|
return sslContext.getSocketFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager,
|
||||||
|
CookieJar cookieJar) {
|
||||||
|
return new OkHttpClient.Builder()
|
||||||
|
.addNetworkInterceptor(getLogInterceptor())
|
||||||
|
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
|
||||||
|
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
.connectTimeout(HttpConstants.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||||
|
.followRedirects(false)
|
||||||
|
.sslSocketFactory(sslSocketFactory, trustManager)
|
||||||
|
.hostnameVerifier((asdf, usdf) -> true)
|
||||||
|
.cookieJar(cookieJar)
|
||||||
|
.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 sContext;
|
||||||
}
|
}
|
||||||
@ -140,17 +141,6 @@ public class HttpClient {
|
|||||||
sContext = context;
|
sContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LogInterceptor getLogInterceptor() {
|
|
||||||
if (sLogInterceptor == null) {
|
|
||||||
sLogInterceptor = new LogInterceptor();
|
|
||||||
}
|
|
||||||
return sLogInterceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
|
|
||||||
return sCookieStore.get(httpUrl.host());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearCookies() {
|
public void clearCookies() {
|
||||||
sCookieStore.clear();
|
sCookieStore.clear();
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,12 @@ public class HttpConstants {
|
|||||||
public static final String ACCEPT_ENCODING_IDENTITY = "identity";
|
public static final String ACCEPT_ENCODING_IDENTITY = "identity";
|
||||||
public static final String OC_FILE_REMOTE_ID = "OC-FileId";
|
public static final String OC_FILE_REMOTE_ID = "OC-FileId";
|
||||||
|
|
||||||
|
// OAuth
|
||||||
|
public static final String OAUTH_HEADER_AUTHORIZATION_CODE = "code";
|
||||||
|
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_REFRESH_TOKEN = "refresh_token";
|
||||||
|
|
||||||
/***********************************************************************************************************
|
/***********************************************************************************************************
|
||||||
************************************************ CONTENT TYPES ********************************************
|
************************************************ CONTENT TYPES ********************************************
|
||||||
***********************************************************************************************************/
|
***********************************************************************************************************/
|
||||||
|
@ -258,11 +258,6 @@ public abstract class RemoteOperation<T> implements Runnable {
|
|||||||
|
|
||||||
final RemoteOperationResult resultToSend = runOperation();
|
final RemoteOperationResult resultToSend = runOperation();
|
||||||
|
|
||||||
if (mAccount != null && mContext != null) {
|
|
||||||
// Save Client Cookies
|
|
||||||
AccountUtils.saveClient(mClient, mAccount, mContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mListenerHandler != null && mListener != null) {
|
if (mListenerHandler != null && mListener != null) {
|
||||||
mListenerHandler.post(() ->
|
mListenerHandler.post(() ->
|
||||||
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend));
|
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend));
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
*
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* 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.oauth
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.oauth.responses.OIDCDiscoveryResponse
|
||||||
|
import com.squareup.moshi.JsonAdapter
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get OIDC Discovery
|
||||||
|
*
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*/
|
||||||
|
class GetOIDCDiscoveryRemoteOperation : RemoteOperation<OIDCDiscoveryResponse>() {
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<OIDCDiscoveryResponse> {
|
||||||
|
try {
|
||||||
|
val uriBuilder = client.baseUri.buildUpon().apply {
|
||||||
|
appendPath(WELL_KNOWN_PATH) // avoid starting "/" in this method
|
||||||
|
appendPath(OPENID_CONFIGURATION_RESOURCE)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
val getMethod = GetMethod(URL(uriBuilder.toString())).apply {
|
||||||
|
addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(getMethod)
|
||||||
|
|
||||||
|
val responseBody = getMethod.getResponseBodyAsString()
|
||||||
|
|
||||||
|
if (status == HttpConstants.HTTP_OK && responseBody != null) {
|
||||||
|
Timber.d("Successful response $responseBody")
|
||||||
|
|
||||||
|
// Parse the response
|
||||||
|
val moshi: Moshi = Moshi.Builder().build()
|
||||||
|
val jsonAdapter: JsonAdapter<OIDCDiscoveryResponse> = moshi.adapter(OIDCDiscoveryResponse::class.java)
|
||||||
|
val oidcDiscoveryResponse: OIDCDiscoveryResponse? = jsonAdapter.fromJson(responseBody)
|
||||||
|
Timber.d("Get OIDC Discovery completed and parsed to [$oidcDiscoveryResponse]")
|
||||||
|
|
||||||
|
return RemoteOperationResult<OIDCDiscoveryResponse>(RemoteOperationResult.ResultCode.OK).apply {
|
||||||
|
data = oidcDiscoveryResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Timber.e("Failed response while getting OIDC server discovery from the server status code: $status; response message: $responseBody")
|
||||||
|
|
||||||
|
return RemoteOperationResult<OIDCDiscoveryResponse>(getMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Exception while getting OIDC server discovery")
|
||||||
|
|
||||||
|
return RemoteOperationResult<OIDCDiscoveryResponse>(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val WELL_KNOWN_PATH = ".well-known"
|
||||||
|
private const val OPENID_CONFIGURATION_RESOURCE = "openid-configuration"
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
*
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*
|
||||||
|
* 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.oauth
|
||||||
|
|
||||||
|
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.operations.RemoteOperation
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.oauth.params.ClientRegistrationParams
|
||||||
|
import com.owncloud.android.lib.resources.oauth.responses.ClientRegistrationResponse
|
||||||
|
import com.squareup.moshi.JsonAdapter
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
class RegisterClientRemoteOperation(
|
||||||
|
private val clientRegistrationParams: ClientRegistrationParams
|
||||||
|
) : RemoteOperation<ClientRegistrationResponse>() {
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<ClientRegistrationResponse> {
|
||||||
|
try {
|
||||||
|
val requestBody = clientRegistrationParams.toRequestBody()
|
||||||
|
|
||||||
|
val postMethod = PostMethod(
|
||||||
|
url = URL(clientRegistrationParams.registrationEndpoint),
|
||||||
|
postRequestBody = requestBody
|
||||||
|
)
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(postMethod)
|
||||||
|
|
||||||
|
val responseBody = postMethod.getResponseBodyAsString()
|
||||||
|
|
||||||
|
if (status == HttpConstants.HTTP_CREATED && responseBody != null) {
|
||||||
|
Timber.d("Successful response $responseBody")
|
||||||
|
|
||||||
|
// Parse the response
|
||||||
|
val moshi: Moshi = Moshi.Builder().build()
|
||||||
|
val jsonAdapter: JsonAdapter<ClientRegistrationResponse> =
|
||||||
|
moshi.adapter(ClientRegistrationResponse::class.java)
|
||||||
|
val clientRegistrationResponse: ClientRegistrationResponse? = jsonAdapter.fromJson(responseBody)
|
||||||
|
Timber.d("Client registered and parsed to $clientRegistrationResponse")
|
||||||
|
|
||||||
|
return RemoteOperationResult<ClientRegistrationResponse>(RemoteOperationResult.ResultCode.OK).apply {
|
||||||
|
data = clientRegistrationResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Timber.e("Failed response while registering a new client. Status code: $status; response message: $responseBody")
|
||||||
|
return RemoteOperationResult<ClientRegistrationResponse>(postMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Exception while registering a new client.")
|
||||||
|
return RemoteOperationResult<ClientRegistrationResponse>(e)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
*
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* 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.oauth
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
|
||||||
|
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.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.oauth.params.TokenRequestParams
|
||||||
|
import com.owncloud.android.lib.resources.oauth.responses.TokenResponse
|
||||||
|
import com.squareup.moshi.JsonAdapter
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform token request
|
||||||
|
*
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*/
|
||||||
|
class TokenRequestRemoteOperation(
|
||||||
|
private val tokenRequestParams: TokenRequestParams
|
||||||
|
) : RemoteOperation<TokenResponse>() {
|
||||||
|
|
||||||
|
override fun run(client: OwnCloudClient): RemoteOperationResult<TokenResponse> {
|
||||||
|
try {
|
||||||
|
val requestBody = tokenRequestParams.toRequestBody()
|
||||||
|
|
||||||
|
val postMethod = PostMethod(URL(tokenRequestParams.tokenEndpoint), requestBody)
|
||||||
|
|
||||||
|
postMethod.addRequestHeader(AUTHORIZATION_HEADER, tokenRequestParams.clientAuth)
|
||||||
|
|
||||||
|
val status = client.executeHttpMethod(postMethod)
|
||||||
|
|
||||||
|
val responseBody = postMethod.getResponseBodyAsString()
|
||||||
|
|
||||||
|
if (status == HTTP_OK && responseBody != null) {
|
||||||
|
Timber.d("Successful response $responseBody")
|
||||||
|
|
||||||
|
// Parse the response
|
||||||
|
val moshi: Moshi = Moshi.Builder().build()
|
||||||
|
val jsonAdapter: JsonAdapter<TokenResponse> = moshi.adapter(TokenResponse::class.java)
|
||||||
|
val tokenResponse: TokenResponse? = jsonAdapter.fromJson(responseBody)
|
||||||
|
Timber.d("Get tokens completed and parsed to $tokenResponse")
|
||||||
|
|
||||||
|
return RemoteOperationResult<TokenResponse>(RemoteOperationResult.ResultCode.OK).apply {
|
||||||
|
data = tokenResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Timber.e("Failed response while getting tokens from the server status code: $status; response message: $responseBody")
|
||||||
|
return RemoteOperationResult<TokenResponse>(postMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Exception while getting tokens")
|
||||||
|
return RemoteOperationResult<TokenResponse>(e)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
*
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*
|
||||||
|
* 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.oauth.params
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_JSON
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
data class ClientRegistrationParams(
|
||||||
|
val registrationEndpoint: String,
|
||||||
|
val clientName: String,
|
||||||
|
val redirectUris: List<String>,
|
||||||
|
val tokenEndpointAuthMethod: String,
|
||||||
|
val applicationType: String
|
||||||
|
) {
|
||||||
|
fun toRequestBody(): RequestBody =
|
||||||
|
JSONObject().apply {
|
||||||
|
put(PARAM_APPLICATION_TYPE, applicationType)
|
||||||
|
put(PARAM_CLIENT_NAME, clientName)
|
||||||
|
put(PARAM_REDIRECT_URIS, JSONArray(redirectUris))
|
||||||
|
put(PARAM_TOKEN_ENDPOINT_AUTH_METHOD, tokenEndpointAuthMethod)
|
||||||
|
}.toString().toRequestBody(CONTENT_TYPE_JSON.toMediaType())
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val PARAM_APPLICATION_TYPE = "application_type"
|
||||||
|
private const val PARAM_CLIENT_NAME = "client_name"
|
||||||
|
private const val PARAM_TOKEN_ENDPOINT_AUTH_METHOD = "token_endpoint_auth_method"
|
||||||
|
private const val PARAM_REDIRECT_URIS = "redirect_uris"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/* 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.oauth.params
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.http.HttpConstants
|
||||||
|
import okhttp3.FormBody
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
|
||||||
|
sealed class TokenRequestParams(
|
||||||
|
val tokenEndpoint: String,
|
||||||
|
val clientAuth: String,
|
||||||
|
val grantType: String
|
||||||
|
) {
|
||||||
|
abstract fun toRequestBody(): RequestBody
|
||||||
|
|
||||||
|
class Authorization(
|
||||||
|
tokenEndpoint: String,
|
||||||
|
clientAuth: String,
|
||||||
|
grantType: String,
|
||||||
|
val authorizationCode: String,
|
||||||
|
val redirectUri: String
|
||||||
|
) : TokenRequestParams(tokenEndpoint, clientAuth, grantType) {
|
||||||
|
|
||||||
|
override fun toRequestBody(): RequestBody =
|
||||||
|
FormBody.Builder()
|
||||||
|
.add(HttpConstants.OAUTH_HEADER_AUTHORIZATION_CODE, authorizationCode)
|
||||||
|
.add(HttpConstants.OAUTH_HEADER_GRANT_TYPE, grantType)
|
||||||
|
.add(HttpConstants.OAUTH_HEADER_REDIRECT_URI, redirectUri)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
class RefreshToken(
|
||||||
|
tokenEndpoint: String,
|
||||||
|
clientAuth: String,
|
||||||
|
grantType: String,
|
||||||
|
val refreshToken: String? = null
|
||||||
|
) : TokenRequestParams(tokenEndpoint, clientAuth, grantType) {
|
||||||
|
|
||||||
|
override fun toRequestBody(): RequestBody =
|
||||||
|
FormBody.Builder().apply {
|
||||||
|
add(HttpConstants.OAUTH_HEADER_GRANT_TYPE, grantType)
|
||||||
|
if (!refreshToken.isNullOrBlank()) {
|
||||||
|
add(HttpConstants.OAUTH_HEADER_REFRESH_TOKEN, refreshToken)
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
*
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*
|
||||||
|
* 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.oauth.responses
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class ClientRegistrationResponse(
|
||||||
|
@Json(name = "client_id")
|
||||||
|
val clientId: String,
|
||||||
|
@Json(name = "client_secret")
|
||||||
|
val clientSecret: String?,
|
||||||
|
@Json(name = "client_id_issued_at")
|
||||||
|
val clientIdIssuedAt: Int?,
|
||||||
|
@Json(name = "client_secret_expires_at")
|
||||||
|
val clientSecretExpiration: Int,
|
||||||
|
)
|
@ -0,0 +1,43 @@
|
|||||||
|
/* ownCloud Android Library is available under MIT license
|
||||||
|
*
|
||||||
|
* @author Abel García de Prada
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 ownCloud GmbH.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* 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.oauth.responses
|
||||||
|
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class OIDCDiscoveryResponse(
|
||||||
|
val authorization_endpoint: String,
|
||||||
|
val check_session_iframe: String,
|
||||||
|
val end_session_endpoint: String,
|
||||||
|
val issuer: String,
|
||||||
|
val registration_endpoint: String,
|
||||||
|
val response_types_supported: List<String>,
|
||||||
|
val scopes_supported: List<String>,
|
||||||
|
val token_endpoint: String,
|
||||||
|
val token_endpoint_auth_methods_supported: List<String>,
|
||||||
|
val userinfo_endpoint: String,
|
||||||
|
)
|
@ -0,0 +1,45 @@
|
|||||||
|
/* 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.oauth.responses
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class TokenResponse(
|
||||||
|
@Json(name = "access_token")
|
||||||
|
val accessToken: String,
|
||||||
|
@Json(name = "expires_in")
|
||||||
|
val expiresIn: Int,
|
||||||
|
@Json(name = "refresh_token")
|
||||||
|
val refreshToken: String?,
|
||||||
|
@Json(name = "token_type")
|
||||||
|
val tokenType: String,
|
||||||
|
@Json(name = "user_id")
|
||||||
|
val userId: String?,
|
||||||
|
val scope: String?,
|
||||||
|
@Json(name = "additional_parameters")
|
||||||
|
val additionalParameters: Map<String, String>?
|
||||||
|
)
|
@ -0,0 +1,47 @@
|
|||||||
|
/* 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.oauth.services
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.oauth.params.ClientRegistrationParams
|
||||||
|
import com.owncloud.android.lib.resources.oauth.params.TokenRequestParams
|
||||||
|
import com.owncloud.android.lib.resources.oauth.responses.ClientRegistrationResponse
|
||||||
|
import com.owncloud.android.lib.resources.oauth.responses.OIDCDiscoveryResponse
|
||||||
|
import com.owncloud.android.lib.resources.oauth.responses.TokenResponse
|
||||||
|
|
||||||
|
interface OIDCService {
|
||||||
|
|
||||||
|
fun getOIDCServerDiscovery(ownCloudClient: OwnCloudClient): RemoteOperationResult<OIDCDiscoveryResponse>
|
||||||
|
|
||||||
|
fun performTokenRequest(
|
||||||
|
ownCloudClient: OwnCloudClient,
|
||||||
|
tokenRequest: TokenRequestParams
|
||||||
|
): RemoteOperationResult<TokenResponse>
|
||||||
|
|
||||||
|
fun registerClientWithRegistrationEndpoint(
|
||||||
|
ownCloudClient: OwnCloudClient,
|
||||||
|
clientRegistrationParams: ClientRegistrationParams
|
||||||
|
): RemoteOperationResult<ClientRegistrationResponse>
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/* 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.oauth.services.implementation
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.oauth.GetOIDCDiscoveryRemoteOperation
|
||||||
|
import com.owncloud.android.lib.resources.oauth.RegisterClientRemoteOperation
|
||||||
|
import com.owncloud.android.lib.resources.oauth.TokenRequestRemoteOperation
|
||||||
|
import com.owncloud.android.lib.resources.oauth.params.ClientRegistrationParams
|
||||||
|
import com.owncloud.android.lib.resources.oauth.params.TokenRequestParams
|
||||||
|
import com.owncloud.android.lib.resources.oauth.responses.ClientRegistrationResponse
|
||||||
|
import com.owncloud.android.lib.resources.oauth.responses.OIDCDiscoveryResponse
|
||||||
|
import com.owncloud.android.lib.resources.oauth.responses.TokenResponse
|
||||||
|
import com.owncloud.android.lib.resources.oauth.services.OIDCService
|
||||||
|
|
||||||
|
class OCOIDCService : OIDCService {
|
||||||
|
|
||||||
|
override fun getOIDCServerDiscovery(
|
||||||
|
ownCloudClient: OwnCloudClient
|
||||||
|
): RemoteOperationResult<OIDCDiscoveryResponse> =
|
||||||
|
GetOIDCDiscoveryRemoteOperation().execute(ownCloudClient)
|
||||||
|
|
||||||
|
override fun performTokenRequest(
|
||||||
|
ownCloudClient: OwnCloudClient,
|
||||||
|
tokenRequest: TokenRequestParams
|
||||||
|
): RemoteOperationResult<TokenResponse> =
|
||||||
|
TokenRequestRemoteOperation(tokenRequest).execute(ownCloudClient)
|
||||||
|
|
||||||
|
override fun registerClientWithRegistrationEndpoint(
|
||||||
|
ownCloudClient: OwnCloudClient,
|
||||||
|
clientRegistrationParams: ClientRegistrationParams
|
||||||
|
): RemoteOperationResult<ClientRegistrationResponse> =
|
||||||
|
RegisterClientRemoteOperation(clientRegistrationParams).execute(ownCloudClient)
|
||||||
|
|
||||||
|
}
|
@ -25,17 +25,15 @@ package com.owncloud.android.lib.resources.status
|
|||||||
|
|
||||||
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.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.common.operations.RemoteOperationResult.ResultCode
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||||
|
import com.owncloud.android.lib.resources.status.HttpScheme.HTTPS_PREFIX
|
||||||
|
import com.owncloud.android.lib.resources.status.HttpScheme.HTTP_PREFIX
|
||||||
|
import com.owncloud.android.lib.resources.status.HttpScheme.HTTP_SCHEME
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.net.URL
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import javax.net.ssl.SSLException
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the server is valid
|
* Checks if the server is valid
|
||||||
@ -45,116 +43,46 @@ import javax.net.ssl.SSLException
|
|||||||
* @author David González Verdugo
|
* @author David González Verdugo
|
||||||
* @author Abel García de Prada
|
* @author Abel García de Prada
|
||||||
*/
|
*/
|
||||||
class GetRemoteStatusOperation : RemoteOperation<OwnCloudVersion>() {
|
class GetRemoteStatusOperation : RemoteOperation<RemoteServerInfo>() {
|
||||||
private lateinit var latestResult: RemoteOperationResult<OwnCloudVersion>
|
|
||||||
|
|
||||||
override fun run(client: OwnCloudClient): RemoteOperationResult<OwnCloudVersion> {
|
public override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> {
|
||||||
|
client.baseUri = buildFullHttpsUrl(client.baseUri)
|
||||||
|
|
||||||
val baseUriStr = client.baseUri.toString()
|
var result = tryToConnect(client)
|
||||||
if (baseUriStr.startsWith(HTTP_PREFIX) || baseUriStr.startsWith(
|
if (!(result.code == ResultCode.OK || result.code == ResultCode.OK_SSL) && !result.isSslRecoverableException) {
|
||||||
HTTPS_PREFIX
|
Timber.d("Establishing secure connection failed, trying non secure connection")
|
||||||
)) {
|
client.baseUri = client.baseUri.buildUpon().scheme(HTTP_SCHEME).build()
|
||||||
tryConnection(client)
|
result = tryToConnect(client)
|
||||||
} else {
|
|
||||||
client.baseUri = Uri.parse(HTTPS_PREFIX + baseUriStr)
|
|
||||||
val httpsSuccess = tryConnection(client)
|
|
||||||
if (!httpsSuccess && !latestResult.isSslRecoverableException) {
|
|
||||||
Timber.d("Establishing secure connection failed, trying non secure connection")
|
|
||||||
client.baseUri = Uri.parse(HTTP_PREFIX + baseUriStr)
|
|
||||||
tryConnection(client)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return latestResult
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryConnection(client: OwnCloudClient): Boolean {
|
private fun tryToConnect(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> {
|
||||||
var successfulConnection = false
|
val baseUrl = client.baseUri.toString()
|
||||||
val baseUrlSt = client.baseUri.toString()
|
client.setFollowRedirects(false)
|
||||||
try {
|
return try {
|
||||||
var getMethod = GetMethod(URL(baseUrlSt + OwnCloudClient.STATUS_PATH)).apply {
|
val requester = StatusRequester()
|
||||||
setReadTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
val requestResult = requester.requestAndFollowRedirects(baseUrl, client)
|
||||||
setConnectionTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
requester.handleRequestResult(requestResult, baseUrl).also {
|
||||||
}
|
client.baseUri = Uri.parse(it.data.baseUrl)
|
||||||
client.setFollowRedirects(false)
|
|
||||||
var isRedirectToNonSecureConnection = false
|
|
||||||
var status: Int
|
|
||||||
try {
|
|
||||||
status = client.executeHttpMethod(getMethod)
|
|
||||||
latestResult =
|
|
||||||
if (isSuccess(status)) RemoteOperationResult(ResultCode.OK)
|
|
||||||
else RemoteOperationResult(getMethod)
|
|
||||||
|
|
||||||
} catch (sslE: SSLException) {
|
|
||||||
latestResult = RemoteOperationResult(sslE)
|
|
||||||
return successfulConnection
|
|
||||||
}
|
|
||||||
|
|
||||||
var redirectedLocation = latestResult.redirectedLocation
|
|
||||||
while (!redirectedLocation.isNullOrEmpty() && !latestResult.isSuccess) {
|
|
||||||
isRedirectToNonSecureConnection =
|
|
||||||
isRedirectToNonSecureConnection ||
|
|
||||||
(baseUrlSt.startsWith(HTTPS_PREFIX) && redirectedLocation.startsWith(
|
|
||||||
HTTP_PREFIX
|
|
||||||
))
|
|
||||||
|
|
||||||
getMethod = GetMethod(URL(redirectedLocation)).apply {
|
|
||||||
setReadTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
|
||||||
setConnectionTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
|
||||||
}
|
|
||||||
|
|
||||||
status = client.executeHttpMethod(getMethod)
|
|
||||||
latestResult = RemoteOperationResult(getMethod)
|
|
||||||
redirectedLocation = latestResult.redirectedLocation
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSuccess(status)) {
|
|
||||||
val respJSON = JSONObject(getMethod.getResponseBodyAsString())
|
|
||||||
if (!respJSON.getBoolean(NODE_INSTALLED)) {
|
|
||||||
latestResult = RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED)
|
|
||||||
} else {
|
|
||||||
val version = respJSON.getString(NODE_VERSION)
|
|
||||||
val ocVersion = OwnCloudVersion(version)
|
|
||||||
// the version object will be returned even if the version is invalid, no error code;
|
|
||||||
// every app will decide how to act if (ocVersion.isVersionValid() == false)
|
|
||||||
latestResult = if (isRedirectToNonSecureConnection) {
|
|
||||||
RemoteOperationResult(ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION)
|
|
||||||
} else {
|
|
||||||
if (baseUrlSt.startsWith(HTTPS_PREFIX)) RemoteOperationResult(ResultCode.OK_SSL)
|
|
||||||
else RemoteOperationResult(ResultCode.OK_NO_SSL)
|
|
||||||
}
|
|
||||||
latestResult.data = ocVersion
|
|
||||||
successfulConnection = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
latestResult = RemoteOperationResult(getMethod)
|
|
||||||
}
|
}
|
||||||
} catch (e: JSONException) {
|
} catch (e: JSONException) {
|
||||||
latestResult = RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED)
|
RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
latestResult = RemoteOperationResult(e)
|
RemoteOperationResult(e)
|
||||||
}
|
}
|
||||||
when {
|
|
||||||
latestResult.isSuccess -> Timber.i("Connection check at $baseUrlSt successful: ${latestResult.logMessage}")
|
|
||||||
|
|
||||||
latestResult.isException ->
|
|
||||||
Timber.e(latestResult.exception, "Connection check at $baseUrlSt: ${latestResult.logMessage}")
|
|
||||||
|
|
||||||
else -> Timber.e("Connection check at $baseUrlSt failed: ${latestResult.logMessage}")
|
|
||||||
}
|
|
||||||
return successfulConnection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
fun usesHttpOrHttps(uri: Uri) =
|
||||||
* Maximum time to wait for a response from the server when the connection is being tested,
|
uri.toString().startsWith(HTTPS_PREFIX) || uri.toString().startsWith(HTTP_PREFIX)
|
||||||
* in MILLISECONDs.
|
|
||||||
*/
|
fun buildFullHttpsUrl(baseUri: Uri): Uri {
|
||||||
private const val TRY_CONNECTION_TIMEOUT: Long = 5000
|
if (usesHttpOrHttps(baseUri)) {
|
||||||
private const val NODE_INSTALLED = "installed"
|
return baseUri
|
||||||
private const val NODE_VERSION = "version"
|
}
|
||||||
private const val HTTPS_PREFIX = "https://"
|
return Uri.parse("$HTTPS_PREFIX$baseUri")
|
||||||
private const val HTTP_PREFIX = "http://"
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
/* 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.status
|
||||||
|
|
||||||
|
object HttpScheme {
|
||||||
|
const val HTTP_SCHEME = "http"
|
||||||
|
const val HTTPS_SCHEME = "https"
|
||||||
|
const val HTTP_PREFIX = "$HTTP_SCHEME://"
|
||||||
|
const val HTTPS_PREFIX = "$HTTPS_SCHEME://"
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/* 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.status
|
||||||
|
|
||||||
|
data class RemoteServerInfo(
|
||||||
|
val ownCloudVersion: OwnCloudVersion,
|
||||||
|
val baseUrl: String,
|
||||||
|
val isSecureConnection: Boolean
|
||||||
|
)
|
@ -0,0 +1,156 @@
|
|||||||
|
/* 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.status
|
||||||
|
|
||||||
|
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.RemoteOperationResult
|
||||||
|
import com.owncloud.android.lib.resources.status.HttpScheme.HTTPS_SCHEME
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
internal class StatusRequester {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is ment to detect if a redirect from a secure to an unsecure connection
|
||||||
|
* was made. If only connections from unsecure connections to unsecure connections were made
|
||||||
|
* this function should not return true, because if the whole redirect chain was unsecure
|
||||||
|
* we assume it was a debug setup.
|
||||||
|
*/
|
||||||
|
fun isRedirectedToNonSecureConnection(
|
||||||
|
redirectedToNonSecureLocationBefore: Boolean,
|
||||||
|
baseUrl: String,
|
||||||
|
redirectedUrl: String
|
||||||
|
) = redirectedToNonSecureLocationBefore
|
||||||
|
|| (baseUrl.startsWith(HTTPS_SCHEME)
|
||||||
|
&& !redirectedUrl.startsWith(HTTPS_SCHEME))
|
||||||
|
|
||||||
|
fun updateLocationWithRedirectPath(oldLocation: String, redirectedLocation: String): String {
|
||||||
|
/** Redirection with different endpoint.
|
||||||
|
* When asking for server.com/status.php and redirected to different.one/, we need to ask different.one/status.php
|
||||||
|
*/
|
||||||
|
if (redirectedLocation.endsWith('/')) {
|
||||||
|
return redirectedLocation.trimEnd('/') + OwnCloudClient.STATUS_PATH
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!redirectedLocation.startsWith("/"))
|
||||||
|
return redirectedLocation
|
||||||
|
val oldLocationURL = URL(oldLocation)
|
||||||
|
return URL(oldLocationURL.protocol, oldLocationURL.host, oldLocationURL.port, redirectedLocation).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getGetMethod(url: String): GetMethod {
|
||||||
|
return GetMethod(URL(url)).apply {
|
||||||
|
setReadTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
setConnectionTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class RequestResult(
|
||||||
|
val getMethod: GetMethod,
|
||||||
|
val status: Int,
|
||||||
|
val redirectedToUnsecureLocation: Boolean,
|
||||||
|
val lastLocation: String
|
||||||
|
)
|
||||||
|
|
||||||
|
fun requestAndFollowRedirects(baseLocation: String, client: OwnCloudClient): RequestResult {
|
||||||
|
var currentLocation = baseLocation + OwnCloudClient.STATUS_PATH
|
||||||
|
var redirectedToUnsecureLocation = false
|
||||||
|
var status: Int
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
val getMethod = getGetMethod(currentLocation)
|
||||||
|
|
||||||
|
status = client.executeHttpMethod(getMethod)
|
||||||
|
val result =
|
||||||
|
if (status.isSuccess()) RemoteOperationResult<OwnCloudVersion>(RemoteOperationResult.ResultCode.OK)
|
||||||
|
else RemoteOperationResult(getMethod)
|
||||||
|
|
||||||
|
if (result.redirectedLocation.isNullOrEmpty() || result.isSuccess) {
|
||||||
|
return RequestResult(getMethod, status, redirectedToUnsecureLocation, currentLocation)
|
||||||
|
} else {
|
||||||
|
val nextLocation = updateLocationWithRedirectPath(currentLocation, result.redirectedLocation)
|
||||||
|
redirectedToUnsecureLocation =
|
||||||
|
isRedirectedToNonSecureConnection(
|
||||||
|
redirectedToUnsecureLocation,
|
||||||
|
currentLocation,
|
||||||
|
nextLocation
|
||||||
|
)
|
||||||
|
currentLocation = nextLocation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Int.isSuccess() = this == HttpConstants.HTTP_OK
|
||||||
|
|
||||||
|
fun handleRequestResult(
|
||||||
|
requestResult: RequestResult,
|
||||||
|
baseUrl: String
|
||||||
|
): RemoteOperationResult<RemoteServerInfo> {
|
||||||
|
if (!requestResult.status.isSuccess())
|
||||||
|
return RemoteOperationResult(requestResult.getMethod)
|
||||||
|
|
||||||
|
val respJSON = JSONObject(requestResult.getMethod.getResponseBodyAsString() ?: "")
|
||||||
|
if (!respJSON.getBoolean(NODE_INSTALLED))
|
||||||
|
return RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED)
|
||||||
|
|
||||||
|
val ocVersion = OwnCloudVersion(respJSON.getString(NODE_VERSION))
|
||||||
|
// the version object will be returned even if the version is invalid, no error code;
|
||||||
|
// every app will decide how to act if (ocVersion.isVersionValid() == false)
|
||||||
|
val result: RemoteOperationResult<RemoteServerInfo> =
|
||||||
|
if (requestResult.redirectedToUnsecureLocation) {
|
||||||
|
RemoteOperationResult(RemoteOperationResult.ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION)
|
||||||
|
} else {
|
||||||
|
if (baseUrl.startsWith(HTTPS_SCHEME)) RemoteOperationResult(RemoteOperationResult.ResultCode.OK_SSL)
|
||||||
|
else RemoteOperationResult(RemoteOperationResult.ResultCode.OK_NO_SSL)
|
||||||
|
}
|
||||||
|
val finalUrl = URL(requestResult.lastLocation)
|
||||||
|
val finalBaseUrl = URL(
|
||||||
|
finalUrl.protocol,
|
||||||
|
finalUrl.host,
|
||||||
|
finalUrl.port,
|
||||||
|
finalUrl.file.dropLastWhile { it != '/' }.trimEnd('/')
|
||||||
|
)
|
||||||
|
|
||||||
|
result.data = RemoteServerInfo(
|
||||||
|
ownCloudVersion = ocVersion,
|
||||||
|
baseUrl = finalBaseUrl.toString(),
|
||||||
|
isSecureConnection = finalBaseUrl.protocol.startsWith(HTTPS_SCHEME)
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Maximum time to wait for a response from the server when the connection is being tested,
|
||||||
|
* in milliseconds.
|
||||||
|
*/
|
||||||
|
private const val TRY_CONNECTION_TIMEOUT = 5_000L
|
||||||
|
private const val NODE_INSTALLED = "installed"
|
||||||
|
private const val NODE_VERSION = "version"
|
||||||
|
}
|
||||||
|
}
|
@ -23,11 +23,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.owncloud.android.lib.resources.status.services
|
package com.owncloud.android.lib.resources.status.services
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion
|
import com.owncloud.android.lib.resources.status.RemoteServerInfo
|
||||||
|
|
||||||
interface ServerInfoService {
|
interface ServerInfoService {
|
||||||
fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean>
|
fun checkPathExistence(path: String, isUserLogged: Boolean, client: OwnCloudClient): RemoteOperationResult<Boolean>
|
||||||
|
|
||||||
fun getRemoteStatus(path: String): RemoteOperationResult<OwnCloudVersion>
|
fun getRemoteStatus(path: String, client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo>
|
||||||
}
|
}
|
||||||
|
@ -19,26 +19,28 @@
|
|||||||
|
|
||||||
package com.owncloud.android.lib.resources.status.services.implementation
|
package com.owncloud.android.lib.resources.status.services.implementation
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient
|
import com.owncloud.android.lib.common.OwnCloudClient
|
||||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.getAnonymousCredentials
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||||
import com.owncloud.android.lib.resources.status.services.ServerInfoService
|
|
||||||
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
|
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
|
||||||
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
|
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
|
||||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion
|
import com.owncloud.android.lib.resources.status.RemoteServerInfo
|
||||||
|
import com.owncloud.android.lib.resources.status.services.ServerInfoService
|
||||||
|
|
||||||
class OCServerInfoService : ServerInfoService {
|
class OCServerInfoService : ServerInfoService {
|
||||||
override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean> =
|
|
||||||
|
override fun checkPathExistence(
|
||||||
|
path: String,
|
||||||
|
isUserLogged: Boolean,
|
||||||
|
client: OwnCloudClient
|
||||||
|
): RemoteOperationResult<Boolean> =
|
||||||
CheckPathExistenceRemoteOperation(
|
CheckPathExistenceRemoteOperation(
|
||||||
remotePath = path,
|
remotePath = path,
|
||||||
isUserLogged = true
|
isUserLogged = true
|
||||||
).execute(createClientFromPath(path))
|
).execute(client)
|
||||||
|
|
||||||
override fun getRemoteStatus(path: String): RemoteOperationResult<OwnCloudVersion> =
|
override fun getRemoteStatus(
|
||||||
GetRemoteStatusOperation().execute(createClientFromPath(path))
|
path: String,
|
||||||
|
client: OwnCloudClient
|
||||||
private fun createClientFromPath(path: String): OwnCloudClient {
|
): RemoteOperationResult<RemoteServerInfo> =
|
||||||
return OwnCloudClient(Uri.parse(path)).apply { credentials = getAnonymousCredentials() }
|
GetRemoteStatusOperation().execute(client)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.common.http.CookieJarImpl
|
||||||
|
import okhttp3.Cookie
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class CookieJarImplTest {
|
||||||
|
|
||||||
|
private val oldCookies = listOf(COOKIE_A, COOKIE_B_OLD)
|
||||||
|
private val newCookies = listOf(COOKIE_B_NEW)
|
||||||
|
private val updatedCookies = listOf(COOKIE_A, COOKIE_B_NEW)
|
||||||
|
private val cookieStore = hashMapOf(SOME_HOST to oldCookies)
|
||||||
|
|
||||||
|
private val cookieJarImpl = CookieJarImpl(cookieStore)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `contains cookie with name - ok - true`() {
|
||||||
|
assertTrue(cookieJarImpl.containsCookieWithName(oldCookies, COOKIE_B_OLD.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `contains cookie with name - ok - false`() {
|
||||||
|
assertFalse(cookieJarImpl.containsCookieWithName(newCookies, COOKIE_A.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `get updated cookies - ok`() {
|
||||||
|
val generatedUpdatedCookies = cookieJarImpl.getUpdatedCookies(oldCookies, newCookies)
|
||||||
|
assertEquals(2, generatedUpdatedCookies.size)
|
||||||
|
assertEquals(updatedCookies[0], generatedUpdatedCookies[1])
|
||||||
|
assertEquals(updatedCookies[1], generatedUpdatedCookies[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `store cookie via saveFromResponse - ok`() {
|
||||||
|
cookieJarImpl.saveFromResponse(SOME_URL, newCookies)
|
||||||
|
val generatedUpdatedCookies = cookieStore[SOME_HOST]
|
||||||
|
assertEquals(2, generatedUpdatedCookies?.size)
|
||||||
|
assertEquals(updatedCookies[0], generatedUpdatedCookies?.get(1))
|
||||||
|
assertEquals(updatedCookies[1], generatedUpdatedCookies?.get(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `load for request - ok`() {
|
||||||
|
val cookies = cookieJarImpl.loadForRequest(SOME_URL)
|
||||||
|
assertEquals(oldCookies[0], cookies[0])
|
||||||
|
assertEquals(oldCookies[1], cookies[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val SOME_HOST = "some.host.com"
|
||||||
|
val SOME_URL = "https://$SOME_HOST".toHttpUrl()
|
||||||
|
val COOKIE_A = Cookie.parse(SOME_URL, "CookieA=CookieValueA")!!
|
||||||
|
val COOKIE_B_OLD = Cookie.parse(SOME_URL, "CookieB=CookieOldValueB")!!
|
||||||
|
val COOKIE_B_NEW = Cookie.parse(SOME_URL, "CookieB=CookieNewValueB")!!
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
|
||||||
|
import com.owncloud.android.lib.resources.status.HttpScheme.HTTPS_PREFIX
|
||||||
|
import com.owncloud.android.lib.resources.status.HttpScheme.HTTP_PREFIX
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(sdk = [Build.VERSION_CODES.O], manifest = Config.NONE)
|
||||||
|
class GetRemoteStatusOperationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `uses http or https - ok - http`() {
|
||||||
|
assertTrue(GetRemoteStatusOperation.usesHttpOrHttps(Uri.parse(HTTP_SOME_OWNCLOUD)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `uses http or https - ok - https`() {
|
||||||
|
assertTrue(GetRemoteStatusOperation.usesHttpOrHttps(Uri.parse(HTTPS_SOME_OWNCLOUD)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `uses http or https - ok - no http or https`() {
|
||||||
|
assertFalse(GetRemoteStatusOperation.usesHttpOrHttps(Uri.parse(SOME_OWNCLOUD)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `build full https url - ok - http`() {
|
||||||
|
assertEquals(
|
||||||
|
Uri.parse(HTTP_SOME_OWNCLOUD),
|
||||||
|
GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(HTTP_SOME_OWNCLOUD))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `build full https url - ok - https`() {
|
||||||
|
assertEquals(
|
||||||
|
Uri.parse(HTTPS_SOME_OWNCLOUD),
|
||||||
|
GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(HTTPS_SOME_OWNCLOUD))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `build full https url - ok - no prefix`() {
|
||||||
|
assertEquals(
|
||||||
|
Uri.parse(HTTPS_SOME_OWNCLOUD),
|
||||||
|
GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(SOME_OWNCLOUD))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `build full https url - ok - no https with subdir`() {
|
||||||
|
assertEquals(
|
||||||
|
Uri.parse(HTTPS_SOME_OWNCLOUD_WITH_SUBDIR),
|
||||||
|
GetRemoteStatusOperation.buildFullHttpsUrl(
|
||||||
|
Uri.parse(HTTPS_SOME_OWNCLOUD_WITH_SUBDIR)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `build full https url - ok - no prefix with subdir`() {
|
||||||
|
assertEquals(
|
||||||
|
Uri.parse(HTTPS_SOME_OWNCLOUD_WITH_SUBDIR),
|
||||||
|
GetRemoteStatusOperation.buildFullHttpsUrl(
|
||||||
|
Uri.parse(SOME_OWNCLOUD_WITH_SUBDIR)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `build full https url - ok - ip`() {
|
||||||
|
assertEquals(Uri.parse(HTTPS_SOME_IP), GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(SOME_IP)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `build full https url - ok - http ip`() {
|
||||||
|
assertEquals(Uri.parse(HTTP_SOME_IP), GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(HTTP_SOME_IP)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `build full https url - ok - ip with port`() {
|
||||||
|
assertEquals(
|
||||||
|
Uri.parse(HTTPS_SOME_IP_WITH_PORT),
|
||||||
|
GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(SOME_IP_WITH_PORT))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `build full https url - ok - ip with http and port`() {
|
||||||
|
assertEquals(
|
||||||
|
Uri.parse(HTTP_SOME_IP_WITH_PORT),
|
||||||
|
GetRemoteStatusOperation.buildFullHttpsUrl(Uri.parse(HTTP_SOME_IP_WITH_PORT))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val SOME_OWNCLOUD = "some_owncloud.com"
|
||||||
|
const val HTTP_SOME_OWNCLOUD = "$HTTP_PREFIX$SOME_OWNCLOUD"
|
||||||
|
const val HTTPS_SOME_OWNCLOUD = "$HTTPS_PREFIX$SOME_OWNCLOUD"
|
||||||
|
|
||||||
|
const val SOME_OWNCLOUD_WITH_SUBDIR = "some_owncloud.com/subdir"
|
||||||
|
const val HTTP_SOME_OWNCLOUD_WITH_SUBDIR = "$HTTP_PREFIX$SOME_OWNCLOUD_WITH_SUBDIR"
|
||||||
|
const val HTTPS_SOME_OWNCLOUD_WITH_SUBDIR = "$HTTPS_PREFIX$SOME_OWNCLOUD_WITH_SUBDIR"
|
||||||
|
|
||||||
|
const val SOME_IP = "184.123.185.12"
|
||||||
|
const val HTTP_SOME_IP = "$HTTP_PREFIX$SOME_IP"
|
||||||
|
const val HTTPS_SOME_IP = "$HTTPS_PREFIX$SOME_IP"
|
||||||
|
|
||||||
|
const val SOME_IP_WITH_PORT = "184.123.185.12:5678"
|
||||||
|
const val HTTP_SOME_IP_WITH_PORT = "$HTTP_PREFIX$SOME_IP_WITH_PORT"
|
||||||
|
const val HTTPS_SOME_IP_WITH_PORT = "$HTTPS_PREFIX$SOME_IP_WITH_PORT"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
import com.owncloud.android.lib.resources.status.StatusRequester
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class StatusRequesterTest {
|
||||||
|
private val requester = StatusRequester()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `update location - ok - absolute path`() {
|
||||||
|
val newLocation = requester.updateLocationWithRedirectPath(TEST_DOMAIN, "$TEST_DOMAIN$SUB_PATH")
|
||||||
|
assertEquals("$TEST_DOMAIN$SUB_PATH", newLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `update location - ok - smaller absolute path`() {
|
||||||
|
val newLocation = requester.updateLocationWithRedirectPath("$TEST_DOMAIN$SUB_PATH", TEST_DOMAIN)
|
||||||
|
assertEquals(TEST_DOMAIN, newLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `update location - ok - relative path`() {
|
||||||
|
val newLocation = requester.updateLocationWithRedirectPath(TEST_DOMAIN, SUB_PATH)
|
||||||
|
assertEquals("$TEST_DOMAIN$SUB_PATH", newLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `update location - ok - replace relative path`() {
|
||||||
|
val newLocation = requester.updateLocationWithRedirectPath("$TEST_DOMAIN/some/other/subdir", SUB_PATH)
|
||||||
|
assertEquals("$TEST_DOMAIN$SUB_PATH", newLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check redirect to unsecure connection - ok - redirect to http`() {
|
||||||
|
assertTrue(
|
||||||
|
requester.isRedirectedToNonSecureConnection(false, SECURE_DOMAIN, UNSECURE_DOMAIN
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check redirect to unsecure connection - ko - redirect to https from http`() {
|
||||||
|
assertFalse(
|
||||||
|
requester.isRedirectedToNonSecureConnection(false, UNSECURE_DOMAIN, SECURE_DOMAIN
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check redirect to unsecure connection - ko - from https to https`() {
|
||||||
|
assertFalse(
|
||||||
|
requester.isRedirectedToNonSecureConnection(false, SECURE_DOMAIN, SECURE_DOMAIN)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check redirect to unsecure connection - ok - from https to https with previous http`() {
|
||||||
|
assertTrue(
|
||||||
|
requester.isRedirectedToNonSecureConnection(true, SECURE_DOMAIN, SECURE_DOMAIN)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check redirect to unsecure connection - ok - from http to http`() {
|
||||||
|
assertFalse(
|
||||||
|
requester.isRedirectedToNonSecureConnection(false, UNSECURE_DOMAIN, UNSECURE_DOMAIN)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TEST_DOMAIN = "https://cloud.somewhere.com"
|
||||||
|
const val SUB_PATH = "/subdir"
|
||||||
|
|
||||||
|
const val SECURE_DOMAIN = "https://cloud.somewhere.com"
|
||||||
|
const val UNSECURE_DOMAIN = "http://somewhereelse.org"
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user