mirror of
				https://github.com/owncloud/android-library.git
				synced 2025-10-31 10:27:45 +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