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

Compare commits

..

No commits in common. "master" and "oc-android-library-0.9.6" have entirely different histories.

309 changed files with 14256 additions and 13384 deletions

9
.classpath Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

View File

@ -1,5 +0,0 @@
[*]
max_line_length = 150
[*.{kt, kts}]
disabled_rules=no-consecutive-blank-lines,no-wildcard-imports,max-line-length,no-blank-line-before-rbrace,final-newline,indent,no-multi-spaces,comment-spacing,parameter-list-wrapping

View File

@ -1,17 +0,0 @@
# 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"

View File

@ -1,10 +0,0 @@
name: "Validate Gradle Wrapper"
on: [pull_request]
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1

15
.gitignore vendored
View File

@ -2,9 +2,6 @@
*.apk
*.ap_
.idea/*
!.idea/codeStyles/
# files for the dex VM
*.dex
@ -12,13 +9,25 @@
*.class
# generated files
bin/
build/
gen/
target/
*.iml
# Local configuration files (sdk path, etc)
.idea/
.gradle/
local.properties
sample_client/local.properties
tests/local.properties
tests/test_cases/local.properties
# Mac .DS_Store files
.DS_Store
# Proguard README
proguard-project.txt
sample_client/proguard-project.txt
tests/proguard-project.txt
tests/test_cases/proguard-project.txt

View File

@ -1,378 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="RIGHT_MARGIN" value="150" />
<JavaCodeStyleSettings>
<option name="FIELD_NAME_PREFIX" value="m" />
<option name="STATIC_FIELD_NAME_PREFIX" value="s" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99999" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99999" />
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
</value>
</option>
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<MarkdownNavigatorCodeStyleSettings>
<option name="RIGHT_MARGIN" value="72" />
</MarkdownNavigatorCodeStyleSettings>
<XML>
<option name="XML_ATTRIBUTE_WRAP" value="0" />
<option name="XML_KEEP_BLANK_LINES" value="1" />
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="150" />
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="WRAP_LONG_LINES" value="true" />
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<PUBLIC>true</PUBLIC>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<PUBLIC>true</PUBLIC>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<INITIALIZER_BLOCK>true</INITIALIZER_BLOCK>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<PUBLIC>true</PUBLIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<PUBLIC>true</PUBLIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<FIELD>true</FIELD>
</match>
</rule>
</section>
<section>
<rule>
<match>
<INITIALIZER_BLOCK>true</INITIALIZER_BLOCK>
</match>
</rule>
</section>
<section>
<rule>
<match>
<CONSTRUCTOR>true</CONSTRUCTOR>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD>true</METHOD>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<METHOD>true</METHOD>
</match>
</rule>
</section>
<section>
<rule>
<match>
<ENUM>true</ENUM>
</match>
</rule>
</section>
<section>
<rule>
<match>
<INTERFACE>true</INTERFACE>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<CLASS>true</CLASS>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<CLASS>true</CLASS>
</match>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="FIELD_ANNOTATION_WRAP" value="0" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

33
.project Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ownCloud Android Library</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,4 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.source=1.6

34
.travis.yml Normal file
View File

@ -0,0 +1,34 @@
language: android
android:
components:
- build-tools-20.0.0
- android-19
- android-17
- android-14
- extra-android-support
licenses:
- 'android-sdk-license-5be876d5'
- 'android-sdk-license-598b93a6'
jdk: oraclejdk7
before_install:
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI -c 20M
- emulator -avd test -no-skin -no-audio -no-window &
- rm pom.xml
- android update project -p .
before_script:
- chmod +x ./wait_for_emulator.sh
- ./wait_for_emulator.sh
script:
- ant clean
- ant debug
- cd test_client/tests
- ant acceptance-test
- cd ../..
- ./gradlew clean build
env:
global:
- secure: f4Kms/mzkYRG4Kp8k6hsvG3Y0ztbJnA2J79OBw3VdqJOKVTzwsxMd1Yh325YDYO7I4HeHxGXy0H4p3rAPzIWr/nrOJ4wmcDwQYDQtVjF7S1ARWsX51FrCEV6A9ec2LAqNCQ8ZC0SoGb+HsmpFCE3uKAxRQt+B5MzOZvKNcvYpMA=
- secure: aF4U20Xlu/rfrbxCmoJAiGh1doYTAZ10UEDmajuinT+ZGSJLivuqD7DDY/00sI6IXWg+J1vL+7jJm4JSYusHPg38UHZ4q92k6RmZycW2ATUzZnGT54O5FRnY67MfVwgVpIMK9UOL/6NEciBHEjlIOL0wbKQiJB++1YtBZOQLGL4=
- secure: N+ECSwNg8v2GsAFJ2y/tCiffauHDpN76zuFI2pDqf0fjmCtJZHu4BH5ArXBHjyHKmgn20a/8eZXcwJaH1HsJ80bo7vDJ2miShjGIQ90hPcdmUiB2XVJcew4f04CtvMDH5o7DRt4ykWArlbPL2rhVag0jotlSidolHBwRFnbDhDY=
matrix:
- ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a

36
AndroidManifest.xml Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2015 ownCloud Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.owncloud.android.lib"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
</manifest>

View File

@ -2,7 +2,7 @@
ownCloud Android Library is available under MIT license
Copyright (C) 2020 ownCloud GmbH.
Copyright (C) 2014 ownCloud Inc.
Copyright (C) 2012 Bartek Przybylski
Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@ -19,14 +19,14 @@ __Step 2.__ Define a dependency within your project. For that, access to Propert
The repository holds two main branches with an infinite lifetime:
- stable
- master
- master
- develop
Branch __origin/stable__ is considered the main branch where the source code of HEAD always reflects a production-ready state.
Branch __origin/master__ is considered the main branch where the source code of HEAD always reflects a production-ready state.
Branch __origin/master__ is considered the main branch where the source code of HEAD always reflects a state with the latest delivered development changes for the next release.
Branch __origin/develop__ is considered the main branch where the source code of HEAD always reflects a state with the latest delivered development changes for the next release.
When the source code in the master branch reaches a stable point and is ready to be released, all of the changes should be merged back into stable somehow and then tagged with a release number.
When the source code in the develop branch reaches a stable point and is ready to be released, all of the changes should be merged back into master somehow and then tagged with a release number.
Other branches, some supporting branches are used to aid parallel development between team members, ease tracking of features, prepare for production releases and to assist in quickly fixing live production problems. Unlike the main branches, these branches always have a limited life time, since they will be removed eventually.
@ -35,8 +35,9 @@ The different types of branches we may use are:
- Branch __perNewFeature__
- Branch __releaseBranches__
Both of them branch off from master and must merge back into master branch through a Pull Request in Github. Once the PR is approved and merged, the US branch may be deleted.
Both of them branch off from develop and must merge back into develop branch through a Pull Request in Github. Once the PR is approved and merged, the US branch may be deleted.
Source: http://nvie.com/posts/a-successful-git-branching-model
### License
@ -44,11 +45,13 @@ ownCloud Android Library is available under MIT license. See the file LICENSE.md
#### Third party libraries
ownCloud Android Library uses OkHttp version 4.6.0, licensed under Apache License and version 2.0. Besides, it uses Dav4Android, licensed under Mozilla Public License, v. 2.0
ownCloud Android Library uses Apache JackRabbit, version 2.2.5. Copyright (C) 2004-2010 The Apache Software Foundation. Licensed under Apache License, Version 2.0.
Apache JackRabbit depends on Commons HTTPClient version 3.1 and SLF4j version 1.7.5; both included also. Copyright (C) 2004-2010 The Apache Software Foundation. Licensed under Apache License, Version 2.0.
### Compatibility
ownCloud Android Library is valid for Android systems from version Android 6 (android:minSdkVersion="23" android:targetSdkVersion="33").
ownCloud Android Library is valid for Android systems from version Android 2.2 (android:minSdkVersion="8" android:targetSdkVersion="19").
ownCloud Android library supports ownCloud server from version 4.5.

View File

@ -1,34 +1,54 @@
buildscript {
ext {
orgJetbrainsKotlin = '1.8.10'
comSquareupMoshi = '1.14.0'
}
repositories {
google()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath "org.jlleitschuh.gradle:ktlint-gradle:11.1.0"
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$orgJetbrainsKotlin"
classpath 'com.android.tools.build:gradle:1.0.0'
}
}
apply plugin: 'com.android.library'
plugins {
id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false
repositories {
mavenCentral()
}
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
dependencies {
compile 'commons-httpclient:commons-httpclient:3.1'
compile 'org.apache.jackrabbit:jackrabbit-webdav:2.7.2'
compile 'org.slf4j:slf4j-api:1.7.5'
}
android {
compileSdkVersion 19
buildToolsVersion "20.0.0"
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
// Move the tests to tests/java, tests/res, etc...
instrumentTest.setRoot('tests')
// Move the build types to build-types/<type>
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
// This moves them out of them default location under src/<type>/... which would
// conflict with src/ being used by the main source set.
// Adding new build types or product flavors should be accompanied
// by a similar customization.
debug.setRoot('build-types/debug')
release.setRoot('build-types/release')
}
android {
lintOptions {
abortOnError false
}
}
}
subprojects {
apply plugin: "org.jlleitschuh.gradle.ktlint"
apply plugin: "com.google.devtools.ksp"
}

92
build.xml Normal file
View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="owncloud-android-library" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

View File

@ -1,15 +0,0 @@
#!/bin/bash
check_license_in_file() {
if ! head -n 20 $FILE | grep -q "Permission is hereby granted, free of charge, to any person obtaining a copy"
then
echo "$FILE does not contain a current copyright header"
fi
}
for FILE in $(find owncloudComLibrary/src -name "*.java" -o -name "*.kt")
do
check_license_in_file
done
./gradlew ktlintFormat

7
custom_rules.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules">
<target name="-post-compile">
<echo>Copying jar file for binary distribution</echo>
<copy file="${out.absolute.dir}/classes.jar" toFile="${out.absolute.dir}/${ant.project.name}.jar" />
</target>
</project>

View File

@ -1,3 +0,0 @@
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536M

Binary file not shown.

View File

@ -1,5 +1,6 @@
#Mon Jan 19 11:03:11 CET 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip

298
gradlew vendored
View File

@ -1,129 +1,79 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original 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.
#
#!/usr/bin/env bash
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
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
MAX_FD="maximum"
warn () {
warn ( ) {
echo "$*"
} >&2
}
die () {
die ( ) {
echo
echo "$*"
echo
exit 1
} >&2
}
# 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 ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# 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
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD=$JAVA_HOME/bin/java
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -132,7 +82,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@ -140,95 +90,75 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
exec "$JAVACMD" "$@"
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

53
gradlew.bat vendored
View File

@ -1,19 +1,3 @@
@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
@rem ##########################################################################
@rem
@ -24,23 +8,20 @@
@rem Set local scope for the variables with windows NT shell
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
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
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
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -54,7 +35,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -64,14 +45,34 @@ echo location of your Java installation.
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
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell

Binary file not shown.

Binary file not shown.

BIN
libs/slf4j-api-1.7.5.jar Normal file

Binary file not shown.

View File

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

View File

@ -1,30 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.owncloud.android.lib.common.http
import com.facebook.stetho.okhttp3.StethoInterceptor
object DebugInterceptorFactory {
fun getInterceptor() = StethoInterceptor()
}

View File

@ -1,221 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common
import android.accounts.AccountManager
import android.accounts.AccountsException
import android.content.Context
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
import com.owncloud.android.lib.resources.status.RemoteServerInfo
import org.apache.commons.lang3.exception.ExceptionUtils
import timber.log.Timber
import java.io.IOException
/**
* ConnectionValidator
*
* @author Christian Schabesberger
*/
class ConnectionValidator(
val context: Context,
private val clearCookiesOnValidation: Boolean
) {
fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager, context: Context): Boolean {
try {
var validationRetryCount = 0
val client = OwnCloudClient(baseClient.baseUri, null, false, singleSessionManager, context)
if (clearCookiesOnValidation) {
client.clearCookies()
} else {
client.cookiesForBaseUri = baseClient.cookiesForBaseUri
}
client.account = baseClient.account
client.credentials = baseClient.credentials
while (validationRetryCount < VALIDATION_RETRY_COUNT) {
Timber.d("validationRetryCount %d", validationRetryCount)
var successCounter = 0
var failCounter = 0
client.setFollowRedirects(true)
if (isOwnCloudStatusOk(client)) {
successCounter++
} else {
failCounter++
}
// Skip the part where we try to check if we can access the parts where we have to be logged in... if we are not logged in
if (baseClient.credentials !is OwnCloudAnonymousCredentials) {
client.setFollowRedirects(false)
val contentReply = canAccessRootFolder(client)
if (contentReply.httpCode == HttpConstants.HTTP_OK) {
if (contentReply.data == true) { //if data is true it means that the content reply was ok
successCounter++
} else {
failCounter++
}
} else {
failCounter++
if (contentReply.httpCode == HttpConstants.HTTP_UNAUTHORIZED) {
checkUnauthorizedAccess(client, singleSessionManager, contentReply.httpCode)
}
}
}
if (successCounter >= failCounter) {
baseClient.credentials = client.credentials
baseClient.cookiesForBaseUri = client.cookiesForBaseUri
return true
}
validationRetryCount++
}
Timber.d("Could not authenticate or get valid data from owncloud")
} catch (e: Exception) {
Timber.d(ExceptionUtils.getStackTrace(e))
}
return false
}
private fun isOwnCloudStatusOk(client: OwnCloudClient): Boolean {
val reply = getOwnCloudStatus(client)
// dont check status code. It currently relais on the broken redirect code of the owncloud client
// TODO: Use okhttp redirect and add this check again
// return reply.httpCode == HttpConstants.HTTP_OK &&
return !reply.isException &&
reply.data != null
}
private fun getOwnCloudStatus(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> {
val remoteStatusOperation = GetRemoteStatusOperation()
return remoteStatusOperation.execute(client)
}
private fun canAccessRootFolder(client: OwnCloudClient): RemoteOperationResult<Boolean> {
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation("/", true)
return checkPathExistenceRemoteOperation.execute(client)
}
/**
* Determines if credentials should be invalidated according the to the HTTPS status
* of a network request just performed.
*
* @param httpStatusCode Result of the last request ran with the 'credentials' belows.
* @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or
* cannot be invalidated with the given arguments.
*/
private fun shouldInvalidateAccountCredentials(credentials: OwnCloudCredentials, account: OwnCloudAccount, httpStatusCode: Int): Boolean {
var shouldInvalidateAccountCredentials = httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED
shouldInvalidateAccountCredentials = shouldInvalidateAccountCredentials and // real credentials
(credentials !is OwnCloudAnonymousCredentials)
// test if have all the needed to effectively invalidate ...
shouldInvalidateAccountCredentials =
shouldInvalidateAccountCredentials and (account.savedAccount != null)
Timber.d(
"""Received error: $httpStatusCode,
account: ${account.name}
credentials are real: ${credentials !is OwnCloudAnonymousCredentials},
so we need to invalidate credentials for account ${account.name} : $shouldInvalidateAccountCredentials"""
)
return shouldInvalidateAccountCredentials
}
/**
* Invalidates credentials stored for the given account in the system [AccountManager] and in
* current [SingleSessionManager.getDefaultSingleton] instance.
*
*
* [.shouldInvalidateAccountCredentials] should be called first.
*
*/
private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) {
Timber.i("Invalidating account credentials for account $account")
val am = AccountManager.get(context)
am.invalidateAuthToken(
account.savedAccount.type,
credentials.authToken
)
am.clearPassword(account.savedAccount) // being strict, only needed for Basic Auth credentials
}
/**
* Checks the status code of an execution and decides if should be repeated with fresh credentials.
*
*
* Invalidates current credentials if the request failed as anauthorized.
*
*
* Refresh current credentials if possible, and marks a retry.
*
* @return
*/
private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean {
var credentialsWereRefreshed = false
val account = client.account
val credentials = account.credentials
if (shouldInvalidateAccountCredentials(credentials, account, status)) {
invalidateAccountCredentials(account, credentials)
if (credentials.authTokenCanBeRefreshed()) {
try {
// This command does the actual refresh
Timber.i("Trying to refresh auth token for account $account")
account.loadCredentials(context)
// if mAccount.getCredentials().length() == 0 --> refresh failed
client.credentials = account.credentials
credentialsWereRefreshed = true
} catch (e: AccountsException) {
Timber.e(
e, "Error while trying to refresh auth token for %s\ntrace: %s",
account.savedAccount.name,
ExceptionUtils.getStackTrace(e)
)
} catch (e: IOException) {
Timber.e(
e, "Error while trying to refresh auth token for %s\ntrace: %s",
account.savedAccount.name,
ExceptionUtils.getStackTrace(e)
)
}
if (!credentialsWereRefreshed) {
// if credentials are not refreshed, client must be removed
// from the OwnCloudClientManager to prevent it is reused once and again
Timber.w("Credentials were not refreshed, client will be removed from the Session Manager to prevent using it over and over")
singleSessionManager.removeClientFor(account)
}
}
// else: onExecute will finish with status 401
}
return credentialsWereRefreshed
}
companion object {
private const val VALIDATION_RETRY_COUNT = 3
}
}

View File

@ -1,255 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
* Copyright (C) 2012 Bartek Przybylski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common;
import android.content.Context;
import android.net.Uri;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials;
import com.owncloud.android.lib.common.http.HttpClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import com.owncloud.android.lib.common.utils.RandomUtils;
import okhttp3.Cookie;
import okhttp3.HttpUrl;
import timber.log.Timber;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Locale;
import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER;
import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY;
public class OwnCloudClient extends HttpClient {
public static final String WEBDAV_FILES_PATH_4_0 = "/remote.php/dav/files/";
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_RETRY_COUNT = 2;
private static int sIntanceCounter = 0;
private OwnCloudCredentials mCredentials = null;
private int mInstanceNumber;
private Uri mBaseUri;
private OwnCloudAccount mAccount;
private final ConnectionValidator mConnectionValidator;
private Object mRequestMutex = new Object();
// If set to true a mutex will be used to prevent parallel execution of the execute() method
// if false the execute() method can be called even though the mutex is already aquired.
// This is used for the ConnectionValidator, which has to be able to execute OperationsWhile all "normal" operations net
// to be set on hold.
private final Boolean mSynchronizeRequests;
private SingleSessionManager mSingleSessionManager = null;
private boolean mFollowRedirects = false;
public OwnCloudClient(Uri baseUri,
ConnectionValidator connectionValidator,
boolean synchronizeRequests,
SingleSessionManager singleSessionManager,
Context context) {
super(context);
if (baseUri == null) {
throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL");
}
mBaseUri = baseUri;
mSynchronizeRequests = synchronizeRequests;
mSingleSessionManager = singleSessionManager;
mInstanceNumber = sIntanceCounter++;
Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient");
clearCredentials();
clearCookies();
mConnectionValidator = connectionValidator;
}
public void clearCredentials() {
if (!(mCredentials instanceof OwnCloudAnonymousCredentials)) {
mCredentials = OwnCloudCredentialsFactory.getAnonymousCredentials();
}
}
public int executeHttpMethod(HttpBaseMethod method) throws Exception {
if (mSynchronizeRequests) {
synchronized (mRequestMutex) {
return saveExecuteHttpMethod(method);
}
} else {
return saveExecuteHttpMethod(method);
}
}
private int saveExecuteHttpMethod(HttpBaseMethod method) throws Exception {
int repeatCounter = 0;
int status;
if (mFollowRedirects) {
method.setFollowRedirects(true);
}
boolean retry;
do {
repeatCounter++;
retry = false;
String requestId = RandomUtils.generateRandomUUID();
// Header to allow tracing requests in apache and ownCloud logs
Timber.d("Executing in request with id %s", requestId);
method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId);
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
method.setRequestHeader(HttpConstants.ACCEPT_LANGUAGE_HEADER, Locale.getDefault().getLanguage());
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
if (mCredentials.getHeaderAuth() != null && !mCredentials.getHeaderAuth().isEmpty()) {
method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
}
status = method.execute(this);
if (shouldConnectionValidatorBeCalled(method, status)) {
retry = mConnectionValidator.validate(this, mSingleSessionManager, getContext()); // retry on success fail on no success
} else if (method.getFollowPermanentRedirects() && status == HTTP_MOVED_PERMANENTLY) {
retry = true;
method.setFollowRedirects(true);
}
} while (retry && repeatCounter < MAX_RETRY_COUNT);
return status;
}
private boolean shouldConnectionValidatorBeCalled(HttpBaseMethod method, int status) {
return mConnectionValidator != null && (
(!(mCredentials instanceof OwnCloudAnonymousCredentials) &&
status == HttpConstants.HTTP_UNAUTHORIZED
) || (!mFollowRedirects &&
!method.getFollowRedirects() &&
status == HttpConstants.HTTP_MOVED_TEMPORARILY
)
);
}
/**
* Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation.
*
* @param responseBodyAsStream InputStream with the HTTP response to exhaust.
*/
public void exhaustResponse(InputStream responseBodyAsStream) {
if (responseBodyAsStream != null) {
try {
responseBodyAsStream.close();
} catch (IOException io) {
Timber.e(io, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED");
}
}
}
public Uri getBaseFilesWebDavUri() {
return Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0);
}
public Uri getUserFilesWebDavUri() {
return (mCredentials instanceof OwnCloudAnonymousCredentials || mAccount == null)
? Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0)
: Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0 + AccountUtils.getUserId(
mAccount.getSavedAccount(), getContext()
)
);
}
public Uri getUploadsWebDavUri() {
return mCredentials instanceof OwnCloudAnonymousCredentials
? Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0)
: Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0 + AccountUtils.getUserId(
mAccount.getSavedAccount(), getContext()
)
);
}
public Uri getBaseUri() {
return mBaseUri;
}
/**
* Sets the root URI to the ownCloud server.
* <p>
* Use with care.
*
* @param uri
*/
public void setBaseUri(Uri uri) {
if (uri == null) {
throw new IllegalArgumentException("URI cannot be NULL");
}
mBaseUri = uri;
}
public final OwnCloudCredentials getCredentials() {
return mCredentials;
}
public void setCredentials(OwnCloudCredentials credentials) {
if (credentials != null) {
mCredentials = credentials;
} else {
clearCredentials();
}
}
public void setCookiesForBaseUri(List<Cookie> cookies) {
getOkHttpClient().cookieJar().saveFromResponse(
HttpUrl.parse(mBaseUri.toString()),
cookies
);
}
public List<Cookie> getCookiesForBaseUri() {
return getOkHttpClient().cookieJar().loadForRequest(
HttpUrl.parse(mBaseUri.toString()));
}
public OwnCloudAccount getAccount() {
return mAccount;
}
public void setAccount(OwnCloudAccount account) {
this.mAccount = account;
}
public void setFollowRedirects(boolean followRedirects) {
this.mFollowRedirects = followRedirects;
}
}

View File

@ -1,225 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.net.Uri;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import timber.log.Timber;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author David A. Velasco
* @author masensio
* @author Christian Schabesberger
* @author David González Verdugo
*/
public class SingleSessionManager {
private static SingleSessionManager sDefaultSingleton;
private static String sUserAgent;
private static ConnectionValidator sConnectionValidator;
private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername = new ConcurrentHashMap<>();
private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername = new ConcurrentHashMap<>();
public static SingleSessionManager getDefaultSingleton() {
if (sDefaultSingleton == null) {
sDefaultSingleton = new SingleSessionManager();
}
return sDefaultSingleton;
}
public static void setConnectionValidator(ConnectionValidator connectionValidator) {
sConnectionValidator = connectionValidator;
}
public static ConnectionValidator getConnectionValidator() {
return sConnectionValidator;
}
public static String getUserAgent() {
return sUserAgent;
}
public static void setUserAgent(String userAgent) {
sUserAgent = userAgent;
}
private static OwnCloudClient createOwnCloudClient(Uri uri,
Context context,
ConnectionValidator connectionValidator,
SingleSessionManager singleSessionManager) {
OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true, singleSessionManager, context);
return client;
}
public OwnCloudClient getClientFor(OwnCloudAccount account,
Context context) throws OperationCanceledException,
AuthenticatorException, IOException {
return getClientFor(account, context, getConnectionValidator());
}
public OwnCloudClient getClientFor(OwnCloudAccount account,
Context context,
ConnectionValidator connectionValidator) throws OperationCanceledException,
AuthenticatorException, IOException {
Timber.d("getClientFor starting ");
if (account == null) {
throw new IllegalArgumentException("Cannot get an OwnCloudClient for a null account");
}
OwnCloudClient client = null;
String accountName = account.getName();
String sessionName = account.getCredentials() == null ? "" :
AccountUtils.buildAccountName(account.getBaseUri(), account.getCredentials().getAuthToken());
if (accountName != null) {
client = mClientsWithKnownUsername.get(accountName);
}
boolean reusingKnown = false; // just for logs
if (client == null) {
if (accountName != null) {
client = mClientsWithUnknownUsername.remove(sessionName);
if (client != null) {
Timber.v("reusing client for session %s", sessionName);
mClientsWithKnownUsername.put(accountName, client);
Timber.v("moved client to account %s", accountName);
}
} else {
client = mClientsWithUnknownUsername.get(sessionName);
}
} else {
Timber.v("reusing client for account %s", accountName);
if (client.getAccount() != null &&
client.getAccount().getCredentials() != null &&
(client.getAccount().getCredentials().getAuthToken() == null || client.getAccount().getCredentials().getAuthToken().isEmpty())
) {
Timber.i("Client " + client.getAccount().getName() + " needs to refresh credentials");
//the next two lines are a hack because okHttpclient is used as a singleton instead of being an
//injected instance that can be deleted when required
client.clearCookies();
client.clearCredentials();
client.setAccount(account);
account.loadCredentials(context);
client.setCredentials(account.getCredentials());
Timber.i("Client " + account.getName() + " with credentials size" + client.getAccount().getCredentials().getAuthToken().length());
}
reusingKnown = true;
}
if (client == null) {
// no client to reuse - create a new one
client = createOwnCloudClient(
account.getBaseUri(),
context,
connectionValidator,
this); // TODO remove dependency on OwnCloudClientFactory
//the next two lines are a hack because okHttpclient is used as a singleton instead of being an
//injected instance that can be deleted when required
client.clearCookies();
client.clearCredentials();
client.setAccount(account);
account.loadCredentials(context);
client.setCredentials(account.getCredentials());
if (accountName != null) {
mClientsWithKnownUsername.put(accountName, client);
Timber.v("new client for account %s", accountName);
} else {
mClientsWithUnknownUsername.put(sessionName, client);
Timber.v("new client for session %s", sessionName);
}
} else {
if (!reusingKnown) {
Timber.v("reusing client for session %s", sessionName);
}
keepUriUpdated(account, client);
}
Timber.d("getClientFor finishing ");
return client;
}
public void removeClientFor(OwnCloudAccount account) {
Timber.d("removeClientFor starting ");
if (account == null) {
return;
}
OwnCloudClient client;
String accountName = account.getName();
if (accountName != null) {
client = mClientsWithKnownUsername.remove(accountName);
if (client != null) {
Timber.v("Removed client for account %s", accountName);
return;
} else {
Timber.v("No client tracked for account %s", accountName);
}
}
mClientsWithUnknownUsername.clear();
Timber.d("removeClientFor finishing ");
}
public void refreshCredentialsForAccount(String accountName, OwnCloudCredentials credentials) {
OwnCloudClient ownCloudClient = mClientsWithKnownUsername.get(accountName);
if (ownCloudClient == null) {
return;
}
ownCloudClient.setCredentials(credentials);
mClientsWithKnownUsername.replace(accountName, ownCloudClient);
}
// this method is just a patch; we need to distinguish accounts in the same host but
// different paths; but that requires updating the accountNames for apps upgrading
private void keepUriUpdated(OwnCloudAccount account, OwnCloudClient reusedClient) {
Uri recentUri = account.getBaseUri();
if (!recentUri.equals(reusedClient.getBaseUri())) {
reusedClient.setBaseUri(recentUri);
}
}
}

View File

@ -1,224 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
* Copyright (C) 2012 Bartek Przybylski
*
* 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.accounts;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountsException;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.net.Uri;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import timber.log.Timber;
import java.io.IOException;
public class AccountUtils {
/**
* Constructs full url to host and webdav resource basing on host version
*
* @param context Valid Android {@link Context}, needed to access the {@link AccountManager}
* @param account A stored ownCloud {@link Account}
* @return Full URL to WebDAV endpoint in the server corresponding to 'account'.
* @throws AccountNotFoundException When 'account' is unknown for the AccountManager
*/
public static String getWebDavUrlForAccount(Context context, Account account)
throws AccountNotFoundException {
return getBaseUrlForAccount(context, account) + OwnCloudClient.WEBDAV_FILES_PATH_4_0
+ AccountUtils.getUserId(account, context);
}
/**
* Extracts url server from the account
*
* @param context Valid Android {@link Context}, needed to access the {@link AccountManager}
* @param account A stored ownCloud {@link Account}
* @return Full URL to the server corresponding to 'account', ending in the base path
* common to all API endpoints.
* @throws AccountNotFoundException When 'account' is unknown for the AccountManager
*/
public static String getBaseUrlForAccount(Context context, Account account)
throws AccountNotFoundException {
AccountManager ama = AccountManager.get(context.getApplicationContext());
String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL);
if (baseurl == null) {
throw new AccountNotFoundException(account, "Account not found", null);
}
return baseurl;
}
/**
* Get the username corresponding to an OC account.
*
* @param account An OC account
* @return Username for the given account, extracted from the account.name
*/
public static String getUsernameForAccount(Account account) {
String username = null;
try {
username = account.name.substring(0, account.name.lastIndexOf('@'));
} catch (Exception e) {
Timber.e(e, "Couldn't get a username for the given account");
}
return username;
}
/**
* @return
* @throws IOException
* @throws AuthenticatorException
* @throws OperationCanceledException
*/
public static OwnCloudCredentials getCredentialsForAccount(Context context, Account account)
throws OperationCanceledException, AuthenticatorException, IOException {
OwnCloudCredentials credentials;
AccountManager am = AccountManager.get(context);
String supportsOAuth2 = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_OAUTH2);
boolean isOauth2 = supportsOAuth2 != null && supportsOAuth2.equals(Constants.OAUTH_SUPPORTED_TRUE);
String username = AccountUtils.getUsernameForAccount(account);
if (isOauth2) {
Timber.i("Trying to retrieve credentials for oAuth account" + account.name);
String accessToken = am.blockingGetAuthToken(
account,
AccountTypeUtils.getAuthTokenTypeAccessToken(account.type),
false);
credentials = OwnCloudCredentialsFactory.newBearerCredentials(username, accessToken);
} else {
String password = am.blockingGetAuthToken(
account,
AccountTypeUtils.getAuthTokenTypePass(account.type),
false);
credentials = OwnCloudCredentialsFactory.newBasicCredentials(
username,
password
);
}
return credentials;
}
/**
* Get the user id corresponding to an OC account.
*
* @param account ownCloud account
* @return user id
*/
public static String getUserId(Account account, Context context) {
AccountManager accountMgr = AccountManager.get(context);
return accountMgr.getUserData(account, Constants.KEY_ID);
}
public static String buildAccountNameOld(Uri serverBaseUrl, String username) {
if (serverBaseUrl.getScheme() == null) {
serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString());
}
String accountName = username + "@" + serverBaseUrl.getHost();
if (serverBaseUrl.getPort() >= 0) {
accountName += ":" + serverBaseUrl.getPort();
}
return accountName;
}
public static String buildAccountName(Uri serverBaseUrl, String username) {
if (serverBaseUrl.getScheme() == null) {
serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString());
}
// Remove http:// or https://
String url = serverBaseUrl.toString();
if (url.contains("://")) {
url = url.substring(serverBaseUrl.toString().indexOf("://") + 3);
}
return username + "@" + url;
}
public static class AccountNotFoundException extends AccountsException {
/**
* Generated - should be refreshed every time the class changes!!
*/
private static final long serialVersionUID = -1684392454798508693L;
private Account mFailedAccount;
public AccountNotFoundException(Account failedAccount, String message, Throwable cause) {
super(message, cause);
mFailedAccount = failedAccount;
}
public Account getFailedAccount() {
return mFailedAccount;
}
}
public static class Constants {
/**
* Base url should point to owncloud installation without trailing / ie:
* http://server/path or https://owncloud.server
*/
public static final String KEY_OC_BASE_URL = "oc_base_url";
/**
* Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
*/
// TODO Please review this constants, move them out of the library, the rest of OAuth variables are in data layer
public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";
public static final String OAUTH_SUPPORTED_TRUE = "TRUE";
/**
* OC account version
*/
public static final String KEY_OC_ACCOUNT_VERSION = "oc_account_version";
/**
* User's id
*/
public static final String KEY_ID = "oc_id";
/**
* User's display name
*/
public static final String KEY_DISPLAY_NAME = "oc_display_name";
public static final int ACCOUNT_VERSION = 1;
}
}

View File

@ -1,63 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication;
import com.owncloud.android.lib.common.http.HttpConstants;
public class OwnCloudBearerCredentials implements OwnCloudCredentials {
private String mUsername;
private String mAccessToken;
public OwnCloudBearerCredentials(String username, String accessToken) {
mUsername = username != null ? username : "";
mAccessToken = accessToken != null ? accessToken : "";
}
@Override
public String getUsername() {
// not relevant for authentication, but relevant for informational purposes
return mUsername;
}
@Override
public String getAuthToken() {
return mAccessToken;
}
@Override
public String getHeaderAuth() {
return HttpConstants.BEARER_AUTHORIZATION_KEY + mAccessToken;
}
@Override
public boolean authTokenExpires() {
return true;
}
@Override
public boolean authTokenCanBeRefreshed() {
return true;
}
}

View File

@ -1,79 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication;
public class OwnCloudCredentialsFactory {
public static final String CREDENTIAL_CHARSET = "UTF-8";
private static OwnCloudAnonymousCredentials sAnonymousCredentials;
public static OwnCloudCredentials newBasicCredentials(String username, String password) {
return new OwnCloudBasicCredentials(username, password);
}
public static OwnCloudCredentials newBearerCredentials(String username, String authToken) {
return new OwnCloudBearerCredentials(username, authToken);
}
public static final OwnCloudCredentials getAnonymousCredentials() {
if (sAnonymousCredentials == null) {
sAnonymousCredentials = new OwnCloudAnonymousCredentials();
}
return sAnonymousCredentials;
}
public static final class OwnCloudAnonymousCredentials implements OwnCloudCredentials {
protected OwnCloudAnonymousCredentials() {
}
@Override
public String getAuthToken() {
return "";
}
@Override
public String getHeaderAuth() {
return "";
}
@Override
public boolean authTokenExpires() {
return false;
}
@Override
public boolean authTokenCanBeRefreshed() {
return false;
}
@Override
public String getUsername() {
// no user name
return null;
}
}
}

View File

@ -1,62 +0,0 @@
/* 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 cookieStore: 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> = cookieStore[url.host] ?: ArrayList()
val updatedCookies: List<Cookie> = getUpdatedCookies(currentCookies, cookies)
cookieStore[url.host] = updatedCookies
}
override fun loadForRequest(url: HttpUrl) =
cookieStore[url.host] ?: ArrayList()
}

View File

@ -1,31 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http
import okhttp3.Interceptor
class DummyInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain) = chain.proceed(chain.request())
}

View File

@ -1,147 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http;
import android.content.Context;
import com.owncloud.android.lib.common.network.AdvancedX509TrustManager;
import com.owncloud.android.lib.common.network.NetworkUtils;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.TlsVersion;
import timber.log.Timber;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Client used to perform network operations
*
* @author David González Verdugo
*/
public class HttpClient {
private Context mContext;
private HashMap<String, List<Cookie>> mCookieStore = new HashMap<>();
private LogInterceptor mLogInterceptor = new LogInterceptor();
private OkHttpClient mOkHttpClient = null;
protected HttpClient(Context context) {
if (context == null) {
Timber.e("Context may not be NULL!");
throw new NullPointerException("Context may not be NULL!");
}
mContext = context;
}
public OkHttpClient getOkHttpClient() {
if (mOkHttpClient == null) {
try {
final X509TrustManager trustManager = new AdvancedX509TrustManager(
NetworkUtils.getKnownServersStore(mContext));
final SSLContext sslContext = buildSSLContext();
sslContext.init(null, new TrustManager[]{trustManager}, null);
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
// Automatic cookie handling, NOT PERSISTENT
final CookieJar cookieJar = new CookieJarImpl(mCookieStore);
mOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar);
} catch (NoSuchAlgorithmException nsae) {
Timber.e(nsae, "Could not setup SSL system.");
throw new RuntimeException("Could not setup okHttp client.", nsae);
} catch (Exception e) {
Timber.e(e, "Could not setup okHttp client.");
throw new RuntimeException("Could not setup okHttp client.", e);
}
}
return mOkHttpClient;
}
private SSLContext buildSSLContext() throws NoSuchAlgorithmException {
try {
return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName());
} catch (NoSuchAlgorithmException tlsv13Exception) {
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 OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager,
CookieJar cookieJar) {
return new OkHttpClient.Builder()
.addNetworkInterceptor(getLogInterceptor())
.addNetworkInterceptor(DebugInterceptorFactory.INSTANCE.getInterceptor())
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
.connectTimeout(HttpConstants.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
.followRedirects(false)
.sslSocketFactory(sslSocketFactory, trustManager)
.hostnameVerifier((asdf, usdf) -> true)
.cookieJar(cookieJar)
.build();
}
public Context getContext() {
return mContext;
}
public LogInterceptor getLogInterceptor() {
return mLogInterceptor;
}
public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
return mCookieStore.get(httpUrl.host());
}
public void clearCookies() {
mCookieStore.clear();
}
}

View File

@ -1,222 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http;
/**
* @author David González Verdugo
*/
public class HttpConstants {
/***********************************************************************************************************
*************************************************** HEADERS ***********************************************
***********************************************************************************************************/
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String COOKIE_HEADER = "Cookie";
public static final String BEARER_AUTHORIZATION_KEY = "Bearer ";
public static final String USER_AGENT_HEADER = "User-Agent";
public static final String IF_MATCH_HEADER = "If-Match";
public static final String IF_NONE_MATCH_HEADER = "If-None-Match";
public static final String CONTENT_TYPE_HEADER = "Content-Type";
public static final String ACCEPT_LANGUAGE_HEADER = "Accept-Language";
public static final String CONTENT_LENGTH_HEADER = "Content-Length";
public static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length";
public static final String OC_X_OC_MTIME_HEADER = "X-OC-Mtime";
public static final String OC_X_REQUEST_ID = "X-Request-ID";
public static final String LOCATION_HEADER = "Location";
public static final String LOCATION_HEADER_LOWER = "location";
public static final String CONTENT_TYPE_URLENCODED_UTF8 = "application/x-www-form-urlencoded; charset=utf-8";
public static final String ACCEPT_ENCODING_HEADER = "Accept-Encoding";
public static final String ACCEPT_ENCODING_IDENTITY = "identity";
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";
public static final String OAUTH_HEADER_CODE_VERIFIER = "code_verifier";
/***********************************************************************************************************
************************************************ CONTENT TYPES ********************************************
***********************************************************************************************************/
public static final String CONTENT_TYPE_XML = "application/xml";
public static final String CONTENT_TYPE_JSON = "application/json";
public static final String CONTENT_TYPE_WWW_FORM = "application/x-www-form-urlencoded";
/***********************************************************************************************************
************************************************ ARGUMENTS NAMES ********************************************
***********************************************************************************************************/
public static final String PARAM_FORMAT = "format";
/***********************************************************************************************************
************************************************ ARGUMENTS VALUES ********************************************
***********************************************************************************************************/
public static final String VALUE_FORMAT = "json";
/***********************************************************************************************************
************************************************ STATUS CODES *********************************************
***********************************************************************************************************/
/**
* 1xx Informational
*/
// 100 Continue (HTTP/1.1 - RFC 2616)
public static final int HTTP_CONTINUE = 100;
// 101 Switching Protocols (HTTP/1.1 - RFC 2616)
public static final int HTTP_SWITCHING_PROTOCOLS = 101;
// 102 Processing (WebDAV - RFC 2518)
public static final int HTTP_PROCESSING = 102;
/**
* 2xx Success
*/
// 200 OK (HTTP/1.0 - RFC 1945)
public static final int HTTP_OK = 200;
// 201 Created (HTTP/1.0 - RFC 1945)
public static final int HTTP_CREATED = 201;
// 202 Accepted (HTTP/1.0 - RFC 1945)
public static final int HTTP_ACCEPTED = 202;
// 203 Non Authoritative Information (HTTP/1.1 - RFC 2616)
public static final int HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
// 204 No Content</tt> (HTTP/1.0 - RFC 1945)
public static final int HTTP_NO_CONTENT = 204;
// 205 Reset Content</tt> (HTTP/1.1 - RFC 2616)
public static final int HTTP_RESET_CONTENT = 205;
// 206 Partial Content</tt> (HTTP/1.1 - RFC 2616)
public static final int HTTP_PARTIAL_CONTENT = 206;
//207 Multi-Status (WebDAV - RFC 2518) or 207 Partial Update OK (HTTP/1.1 - draft-ietf-http-v11-spec-rev-01?)
public static final int HTTP_MULTI_STATUS = 207;
/**
* 3xx Redirection
*/
// 300 Mutliple Choices</tt> (HTTP/1.1 - RFC 2616)
public static final int HTTP_MULTIPLE_CHOICES = 300;
// 301 Moved Permanently</tt> (HTTP/1.0 - RFC 1945)
public static final int HTTP_MOVED_PERMANENTLY = 301;
// 302 Moved Temporarily</tt> (Sometimes <tt>Found) (HTTP/1.0 - RFC 1945)
public static final int HTTP_MOVED_TEMPORARILY = 302;
// 303 See Other (HTTP/1.1 - RFC 2616)
public static final int HTTP_SEE_OTHER = 303;
// 304 Not Modified (HTTP/1.0 - RFC 1945)
public static final int HTTP_NOT_MODIFIED = 304;
// 305 Use Proxy (HTTP/1.1 - RFC 2616)
public static final int HTTP_USE_PROXY = 305;
// 307 Temporary Redirect (HTTP/1.1 - RFC 2616)
public static final int HTTP_TEMPORARY_REDIRECT = 307;
/**
* 4xx Client Error
*/
// 400 Bad Request (HTTP/1.1 - RFC 2616)
public static final int HTTP_BAD_REQUEST = 400;
// 401 Unauthorized (HTTP/1.0 - RFC 1945)
public static final int HTTP_UNAUTHORIZED = 401;
// 402 Payment Required (HTTP/1.1 - RFC 2616)
public static final int HTTP_PAYMENT_REQUIRED = 402;
// 403 Forbidden (HTTP/1.0 - RFC 1945)
public static final int HTTP_FORBIDDEN = 403;
// 404 Not Found (HTTP/1.0 - RFC 1945)
public static final int HTTP_NOT_FOUND = 404;
// 405 Method Not Allowed (HTTP/1.1 - RFC 2616)
public static final int HTTP_METHOD_NOT_ALLOWED = 405;
// 406 Not Acceptable (HTTP/1.1 - RFC 2616)
public static final int HTTP_NOT_ACCEPTABLE = 406;
// 407 Proxy Authentication Required (HTTP/1.1 - RFC 2616)
public static final int HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
// 408 Request Timeout (HTTP/1.1 - RFC 2616)
public static final int HTTP_REQUEST_TIMEOUT = 408;
// 409 Conflict (HTTP/1.1 - RFC 2616)
public static final int HTTP_CONFLICT = 409;
// 410 Gone (HTTP/1.1 - RFC 2616)
public static final int HTTP_GONE = 410;
// 411 Length Required (HTTP/1.1 - RFC 2616)
public static final int HTTP_LENGTH_REQUIRED = 411;
// 412 Precondition Failed (HTTP/1.1 - RFC 2616)
public static final int HTTP_PRECONDITION_FAILED = 412;
// 413 Request Entity Too Large (HTTP/1.1 - RFC 2616)
public static final int HTTP_REQUEST_TOO_LONG = 413;
// 414 Request-URI Too Long (HTTP/1.1 - RFC 2616)
public static final int HTTP_REQUEST_URI_TOO_LONG = 414;
// 415 Unsupported Media Type (HTTP/1.1 - RFC 2616)
public static final int HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
// 416 Requested Range Not Satisfiable (HTTP/1.1 - RFC 2616)
public static final int HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
// 417 Expectation Failed (HTTP/1.1 - RFC 2616)
public static final int HTTP_EXPECTATION_FAILED = 417;
// 419 Insufficient Space on Resource (WebDAV - draft-ietf-webdav-protocol-05?)
// or <tt>419 Proxy Reauthentication Required (HTTP/1.1 drafts?)
public static final int HTTP_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
// 420 Method Failure (WebDAV - draft-ietf-webdav-protocol-05?)
public static final int HTTP_METHOD_FAILURE = 420;
// 422 Unprocessable Entity (WebDAV - RFC 2518)
public static final int HTTP_UNPROCESSABLE_ENTITY = 422;
// 423 Locked (WebDAV - RFC 2518)
public static final int HTTP_LOCKED = 423;
// 424 Failed Dependency (WebDAV - RFC 2518)
public static final int HTTP_FAILED_DEPENDENCY = 424;
public static final int HTTP_TOO_EARLY = 425;
/**
* 5xx Client Error
*/
// 500 Server Error (HTTP/1.0 - RFC 1945)
public static final int HTTP_INTERNAL_SERVER_ERROR = 500;
// 501 Not Implemented (HTTP/1.0 - RFC 1945)
public static final int HTTP_NOT_IMPLEMENTED = 501;
// 502 Bad Gateway (HTTP/1.0 - RFC 1945)
public static final int HTTP_BAD_GATEWAY = 502;
// 503 Service Unavailable (HTTP/1.0 - RFC 1945)
public static final int HTTP_SERVICE_UNAVAILABLE = 503;
// 504 Gateway Timeout (HTTP/1.1 - RFC 2616)
public static final int HTTP_GATEWAY_TIMEOUT = 504;
// 505 HTTP Version Not Supported (HTTP/1.1 - RFC 2616)
public static final int HTTP_HTTP_VERSION_NOT_SUPPORTED = 505;
// 507 Insufficient Storage (WebDAV - RFC 2518)
public static final int HTTP_INSUFFICIENT_STORAGE = 507;
/***********************************************************************************************************
*************************************************** TIMEOUTS **********************************************
***********************************************************************************************************/
/**
* Default timeout for waiting data from the server
*/
public static final int DEFAULT_DATA_TIMEOUT = 60000;
/**
* Default timeout for establishing a connection
*/
public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
}

View File

@ -1,65 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.owncloud.android.lib.common.http
import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_JSON
import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_WWW_FORM
import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_XML
import okhttp3.MediaType
import timber.log.Timber
import java.util.Locale
object LogBuilder {
fun logHttp(
networkPetition: NetworkPetition,
networkNode: NetworkNode,
requestId: String? = "",
description: String
) = Timber.d("[Network, $networkPetition] [$networkNode] [$requestId] $description")
}
enum class NetworkPetition {
REQUEST, RESPONSE;
override fun toString(): String = super.toString().lowercase(Locale.ROOT)
}
enum class NetworkNode {
INFO, HEADER, BODY;
override fun toString(): String = super.toString().lowercase(Locale.ROOT)
}
/**
* Check whether a media type is loggable.
*
* @return true if its type is text, xml, json, or x-www-form-urlencoded.
*/
fun MediaType?.isLoggable(): Boolean =
this?.let { mediaType ->
val mediaTypeString = mediaType.toString()
(mediaType.type == "text" ||
mediaTypeString.contains(CONTENT_TYPE_XML) ||
mediaTypeString.contains(CONTENT_TYPE_JSON) ||
mediaTypeString.contains(CONTENT_TYPE_WWW_FORM))
} ?: false

View File

@ -1,179 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http
import com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER
import com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID
import com.owncloud.android.lib.common.http.LogBuilder.logHttp
import com.owncloud.android.lib.common.http.NetworkNode.BODY
import com.owncloud.android.lib.common.http.NetworkNode.HEADER
import com.owncloud.android.lib.common.http.NetworkNode.INFO
import com.owncloud.android.lib.common.http.NetworkPetition.REQUEST
import com.owncloud.android.lib.common.http.NetworkPetition.RESPONSE
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.RequestBody
import okhttp3.Response
import okhttp3.ResponseBody
import okio.Buffer
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import kotlin.math.max
class LogInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
if (!httpLogsEnabled) {
return chain.proceed(chain.request())
}
val request = chain.request().also {
val requestId = it.headers[OC_X_REQUEST_ID]
logHttp(REQUEST, INFO, requestId, "Method: ${it.method} URL: ${it.url}")
logHeaders(requestId, it.headers, REQUEST)
logRequestBody(requestId, it.body)
}
val response = chain.proceed(request)
return response.also {
val requestId = it.request.headers[OC_X_REQUEST_ID]
logHttp(
RESPONSE,
INFO,
requestId,
"Method: ${request.method} URL: ${request.url} Code: ${it.code} Message: ${it.message}"
)
logHeaders(requestId, it.headers, RESPONSE)
logResponseBody(requestId, it.body)
}
}
private fun logHeaders(requestId: String?, headers: Headers, networkPetition: NetworkPetition) {
headers.forEach { header ->
val headerValue: String = if (header.first.equals(AUTHORIZATION_HEADER, true)) {
"[redacted]"
} else {
header.second
}
logHttp(networkPetition, HEADER, requestId, "${header.first}: $headerValue")
}
}
private fun logRequestBody(requestId: String?, requestBodyParam: RequestBody?) {
requestBodyParam?.let { requestBody ->
if (requestBody.isOneShot()) {
logHttp(REQUEST, BODY, requestId, "One shot body -- Omitted")
return@let
}
if (requestBody.isDuplex()) {
logHttp(REQUEST, BODY, requestId, "Duplex body -- Omitted")
return@let
}
val buffer = Buffer()
requestBody.writeTo(buffer)
val contentType = requestBody.contentType()
val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8
logHttp(REQUEST, BODY, requestId, "Length: ${requestBody.contentLength()} byte body")
logHttp(REQUEST, BODY, requestId, "Type: ${requestBody.contentType()}")
logHttp(REQUEST, BODY, requestId, "--> Body start for request")
if (contentType.isLoggable()) {
if (requestBody.contentLength() < LIMIT_BODY_LOG) {
logHttp(REQUEST, BODY, requestId, buffer.readString(charset))
} else {
logHttp(REQUEST, BODY, requestId, buffer.readString(LIMIT_BODY_LOG, charset))
}
logHttp(
REQUEST,
BODY,
requestId,
"<-- Body end for request -- Omitted: ${max(0, requestBody.contentLength() - LIMIT_BODY_LOG)} bytes"
)
} else {
logHttp(
REQUEST,
BODY,
requestId,
"<-- Body end for request -- Binary -- Omitted: ${requestBody.contentLength()} bytes"
)
}
} ?: logHttp(REQUEST, BODY, requestId, "Empty body")
}
private fun logResponseBody(requestId: String?, responseBodyParam: ResponseBody?) {
responseBodyParam?.let { responseBody ->
val contentType = responseBody.contentType()
val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8
logHttp(RESPONSE, BODY, requestId, "Length: ${responseBody.contentLength()} byte body")
logHttp(RESPONSE, BODY, requestId, "Type: ${responseBody.contentType()}")
logHttp(RESPONSE, BODY, requestId, "--> Body start for response")
val source = responseBody.source()
source.request(LIMIT_BODY_LOG)
val buffer = source.buffer
if (contentType.isLoggable()) {
if (responseBody.contentLength() < LIMIT_BODY_LOG) {
logHttp(RESPONSE, BODY, requestId, buffer.clone().readString(charset))
} else {
logHttp(RESPONSE, BODY, requestId, buffer.clone().readString(LIMIT_BODY_LOG, charset))
}
logHttp(
RESPONSE,
BODY,
requestId,
"<-- Body end for response -- Omitted: ${
max(
0,
responseBody.contentLength() - LIMIT_BODY_LOG
)
} bytes"
)
} else {
logHttp(
RESPONSE,
BODY,
requestId,
"<-- Body end for response -- Binary -- Omitted: ${responseBody.contentLength()} bytes"
)
}
} ?: logHttp(RESPONSE, BODY, requestId, "Empty body")
}
companion object {
var httpLogsEnabled: Boolean = false
private const val LIMIT_BODY_LOG: Long = 1024
}
}

View File

@ -1,82 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory mInternalSSLSocketFactory;
public TLSSocketFactory(SSLSocketFactory delegate) {
mInternalSSLSocketFactory = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return mInternalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return mInternalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws
IOException {
return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if((socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2", "TLSv1.3"});
}
return socket;
}
}

View File

@ -1,187 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods
import com.owncloud.android.lib.common.http.HttpClient
import okhttp3.Call
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.InputStream
import java.net.MalformedURLException
import java.net.URL
import java.util.concurrent.TimeUnit
abstract class HttpBaseMethod constructor(url: URL) {
var httpUrl: HttpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException()
var request: Request
var followPermanentRedirects = false
abstract var response: Response
var call: Call? = null
var followRedirects: Boolean = true
var retryOnConnectionFailure: Boolean = true
var connectionTimeoutVal: Long? = null
var connectionTimeoutUnit: TimeUnit? = null
var readTimeoutVal: Long? = null
private set
var readTimeoutUnit: TimeUnit? = null
private set
init {
request = Request.Builder()
.url(httpUrl)
.build()
}
@Throws(Exception::class)
open fun execute(httpClient: HttpClient): Int {
val okHttpClient = httpClient.okHttpClient.newBuilder().apply {
retryOnConnectionFailure(retryOnConnectionFailure)
followRedirects(followRedirects)
readTimeoutUnit?.let { unit ->
readTimeoutVal?.let { readTimeout(it, unit) }
}
connectionTimeoutUnit?.let { unit ->
connectionTimeoutVal?.let { connectTimeout(it, unit) }
}
}.build()
return onExecute(okHttpClient)
}
open fun setUrl(url: HttpUrl) {
request = request.newBuilder()
.url(url)
.build()
}
/****************
*** Requests ***
****************/
fun getRequestHeader(name: String): String? {
return request.header(name)
}
fun getRequestHeadersAsHashMap(): HashMap<String, String?> {
val headers: HashMap<String, String?> = HashMap()
val superHeaders: Set<String> = request.headers.names()
superHeaders.forEach {
headers[it] = getRequestHeader(it)
}
return headers
}
open fun addRequestHeader(name: String, value: String) {
request = request.newBuilder()
.addHeader(name, value)
.build()
}
/**
* Sets a header and replace it if already exists with that name
*
* @param name header name
* @param value header value
*/
open fun setRequestHeader(name: String, value: String) {
request = request.newBuilder()
.header(name, value)
.build()
}
/****************
*** Response ***
****************/
val statusCode: Int
get() = response.code
val statusMessage: String
get() = response.message
// Headers
open fun getResponseHeaders(): Headers? {
return response.headers
}
open fun getResponseHeader(headerName: String): String? {
return response.header(headerName)
}
// Body
fun getResponseBodyAsString(): String? = response.body?.string()
open fun getResponseBodyAsStream(): InputStream? {
return response.body?.byteStream()
}
/**
* returns the final url after following the last redirect.
*/
open fun getFinalUrl() = response.request.url
/*************************
*** Connection Params ***
*************************/
//////////////////////////////
// Setter
//////////////////////////////
// Connection parameters
open fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) {
readTimeoutVal = readTimeout
readTimeoutUnit = timeUnit
}
open fun setConnectionTimeout(
connectionTimeout: Long,
timeUnit: TimeUnit
) {
connectionTimeoutVal = connectionTimeout
connectionTimeoutUnit = timeUnit
}
/************
*** Call ***
************/
open fun abort() {
call?.cancel()
}
open val isAborted: Boolean
get() = call?.isCanceled() ?: false
//////////////////////////////
// For override
//////////////////////////////
@Throws(Exception::class)
protected abstract fun onExecute(okHttpClient: OkHttpClient): Int
}

View File

@ -1,43 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.nonwebdav
import okhttp3.OkHttpClient
import java.io.IOException
import java.net.URL
/**
* OkHttp delete calls wrapper
*
* @author David González Verdugo
*/
class DeleteMethod(url: URL) : HttpMethod(url) {
@Throws(IOException::class)
override fun onExecute(okHttpClient: OkHttpClient): Int {
request = request.newBuilder()
.delete()
.build()
return super.onExecute(okHttpClient)
}
}

View File

@ -1,43 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.nonwebdav
import okhttp3.OkHttpClient
import java.io.IOException
import java.net.URL
/**
* OkHttp get calls wrapper
*
* @author David González Verdugo
*/
class GetMethod(url: URL) : HttpMethod(url) {
@Throws(IOException::class)
override fun onExecute(okHttpClient: OkHttpClient): Int {
request = request.newBuilder()
.get()
.build()
return super.onExecute(okHttpClient)
}
}

View File

@ -1,47 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.nonwebdav
import okhttp3.OkHttpClient
import okhttp3.RequestBody
import java.io.IOException
import java.net.URL
/**
* OkHttp post calls wrapper
*
* @author David González Verdugo
*/
class PostMethod(
url: URL,
private val postRequestBody: RequestBody
) : HttpMethod(url) {
@Throws(IOException::class)
override fun onExecute(okHttpClient: OkHttpClient): Int {
request = request.newBuilder()
.post(postRequestBody)
.build()
return super.onExecute(okHttpClient)
}
}

View File

@ -1,47 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.nonwebdav
import okhttp3.OkHttpClient
import okhttp3.RequestBody
import java.io.IOException
import java.net.URL
/**
* OkHttp put calls wrapper
*
* @author David González Verdugo
*/
class PutMethod(
url: URL,
private val putRequestBody: RequestBody
) : HttpMethod(url) {
@Throws(IOException::class)
override fun onExecute(okHttpClient: OkHttpClient): Int {
request = request.newBuilder()
.put(putRequestBody)
.build()
return super.onExecute(okHttpClient)
}
}

View File

@ -1,52 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.DavOCResource
import okhttp3.Response
import java.net.URL
/**
* Copy calls wrapper
*
* @author Christian Schabesberger
* @author David González Verdugo
*/
class CopyMethod(
val url: URL,
private val destinationUrl: String,
val forceOverride: Boolean = false
) : DavMethod(url) {
@Throws(Exception::class)
public override fun onDavExecute(davResource: DavOCResource): Int {
davResource.copy(
destinationUrl,
forceOverride,
super.getRequestHeadersAsHashMap()
) { callBackResponse: Response ->
response = callBackResponse
}
return super.statusCode
}
}

View File

@ -1,32 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav
/**
* @author David González Verdugo
*/
object DavConstants {
const val DEPTH_0 = 0
const val DEPTH_1 = 1
}

View File

@ -1,97 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.Dav4jvm.log
import at.bitfire.dav4jvm.DavOCResource
import at.bitfire.dav4jvm.exception.HttpException
import at.bitfire.dav4jvm.exception.RedirectException
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
import okhttp3.OkHttpClient
import okhttp3.Protocol
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import java.net.URL
/**
* Wrapper to perform WebDAV (dav4android) calls
*
* @author David González Verdugo
*/
abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) {
override lateinit var response: Response
private var davResource: DavOCResource? = null
override fun abort() {
davResource?.cancelCall()
}
protected abstract fun onDavExecute(davResource: DavOCResource): Int
@Throws(Exception::class)
override fun onExecute(okHttpClient: OkHttpClient): Int {
return try {
davResource = DavOCResource(
okHttpClient.newBuilder().followRedirects(false).build(),
httpUrl,
log
)
onDavExecute(davResource!!)
} catch (httpException: HttpException) {
// Modify responses with information gathered from exceptions
if (httpException is RedirectException) {
response = Response.Builder()
.header(
HttpConstants.LOCATION_HEADER, httpException.redirectLocation
)
.code(httpException.code)
.request(request)
.message(httpException.message ?: "")
.protocol(Protocol.HTTP_1_1)
.build()
} else {
// The check below should be included in okhttp library, method ResponseBody.create(
// TODO check most recent versions of okhttp to see if this is already fixed and try to update if so
if (response.body?.contentType() != null) {
val responseBody = (httpException.responseBody ?: "").toResponseBody(response.body?.contentType())
response = response.newBuilder()
.body(responseBody)
.build()
}
}
httpException.code
}
}
//////////////////////////////
// Getter
//////////////////////////////
override val isAborted: Boolean
get() = davResource?.isCallAborted() ?: false
}

View File

@ -1,60 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.PropertyUtils.getQuotaPropset
import at.bitfire.dav4jvm.property.CreationDate
import at.bitfire.dav4jvm.property.DisplayName
import at.bitfire.dav4jvm.property.GetContentLength
import at.bitfire.dav4jvm.property.GetContentType
import at.bitfire.dav4jvm.property.GetETag
import at.bitfire.dav4jvm.property.GetLastModified
import at.bitfire.dav4jvm.property.OCId
import at.bitfire.dav4jvm.property.OCPermissions
import at.bitfire.dav4jvm.property.OCPrivatelink
import at.bitfire.dav4jvm.property.OCSize
import at.bitfire.dav4jvm.property.ResourceType
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
object DavUtils {
@JvmStatic val allPropSet: Array<Property.Name>
get() = arrayOf(
DisplayName.NAME,
GetContentType.NAME,
ResourceType.NAME,
GetContentLength.NAME,
GetLastModified.NAME,
CreationDate.NAME,
GetETag.NAME,
OCPermissions.NAME,
OCId.NAME,
OCSize.NAME,
OCPrivatelink.NAME,
OCShareTypes.NAME,
)
val quotaPropSet: Array<Property.Name>
get() = getQuotaPropset()
}

View File

@ -1,47 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.DavOCResource
import okhttp3.Response
import java.net.URL
/**
* MkCol calls wrapper
*
* @author Christian Schabesberger
* @author David González Verdugo
*/
class MkColMethod(url: URL) : DavMethod(url) {
@Throws(Exception::class)
public override fun onDavExecute(davResource: DavOCResource): Int {
davResource.mkCol(
xmlBody = null,
listOfHeaders = super.getRequestHeadersAsHashMap()
) { callBackResponse: Response ->
response = callBackResponse
}
return super.statusCode
}
}

View File

@ -1,53 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.DavOCResource
import okhttp3.Response
import java.net.URL
/**
* Move calls wrapper
*
* @author Christian Schabesberger
* @author David González Verdugo
*/
class MoveMethod(
url: URL,
private val destinationUrl: String,
val forceOverride: Boolean = false
) : DavMethod(url) {
@Throws(Exception::class)
override fun onDavExecute(davResource: DavOCResource): Int {
davResource.move(
destinationUrl,
forceOverride,
super.getRequestHeadersAsHashMap()
) { callBackResponse: Response ->
response = callBackResponse
}
return super.statusCode
}
}

View File

@ -1,73 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.DavOCResource
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.Response.HrefRelation
import at.bitfire.dav4jvm.exception.DavException
import java.io.IOException
import java.net.URL
/**
* Propfind calls wrapper
*
* @author David González Verdugo
*/
class PropfindMethod(
url: URL,
private val depth: Int,
private val propertiesToRequest: Array<Property.Name>
) : DavMethod(url) {
// response
val members: MutableList<Response>
var root: Response?
private set
@Throws(IOException::class, DavException::class)
public override fun onDavExecute(davResource: DavOCResource): Int {
davResource.propfind(
depth = depth,
reqProp = propertiesToRequest,
listOfHeaders = super.getRequestHeadersAsHashMap(),
callback = { response: Response, hrefRelation: HrefRelation ->
when (hrefRelation) {
HrefRelation.MEMBER -> members.add(response)
HrefRelation.SELF -> this.root = response
HrefRelation.OTHER -> {
}
}
}, rawCallback = { callBackResponse: okhttp3.Response ->
response = callBackResponse
})
return statusCode
}
init {
members = arrayListOf()
this.root = null
}
}

View File

@ -1,53 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav
import at.bitfire.dav4jvm.DavOCResource
import at.bitfire.dav4jvm.exception.HttpException
import com.owncloud.android.lib.common.http.HttpConstants
import okhttp3.RequestBody
import java.io.IOException
import java.net.URL
/**
* Put calls wrapper
*
* @author David González Verdugo
*/
class PutMethod(
url: URL,
private val putRequestBody: RequestBody
) : DavMethod(url) {
@Throws(IOException::class, HttpException::class)
public override fun onDavExecute(davResource: DavOCResource): Int {
davResource.put(
putRequestBody,
super.getRequestHeader(HttpConstants.IF_MATCH_HEADER),
getRequestHeadersAsHashMap()
) { callBackResponse ->
response = callBackResponse
}
return super.statusCode
}
}

View File

@ -1,44 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav.properties
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.XmlUtils
import org.xmlpull.v1.XmlPullParser
class OCShareTypes : ShareTypeListProperty() {
class Factory : ShareTypeListProperty.Factory() {
override fun create(parser: XmlPullParser) =
create(parser, OCShareTypes())
override fun getName(): Property.Name = NAME
}
companion object {
@JvmField
val NAME = Property.Name(XmlUtils.NS_OWNCLOUD, "share-types")
}
}

View File

@ -1,46 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav.properties
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.PropertyFactory
import at.bitfire.dav4jvm.XmlUtils
import org.xmlpull.v1.XmlPullParser
import java.util.LinkedList
abstract class ShareTypeListProperty : Property {
val shareTypes = LinkedList<String>()
override fun toString() = "share types =[" + shareTypes.joinToString(", ") + "]"
abstract class Factory : PropertyFactory {
fun create(parser: XmlPullParser, list: ShareTypeListProperty): ShareTypeListProperty {
XmlUtils.readTextPropertyList(parser, Property.Name(XmlUtils.NS_OWNCLOUD, "share-type"), list.shareTypes)
return list
}
}
}

View File

@ -1,92 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network
import com.owncloud.android.lib.resources.files.chunks.ChunkedUploadFromFileSystemOperation.Companion.CHUNK_SIZE
import okhttp3.MediaType
import okio.BufferedSink
import timber.log.Timber
import java.io.File
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
/**
* A Request body that represents a file chunk and include information about the progress when uploading it
*
* @author David González Verdugo
*/
class ChunkFromFileRequestBody(
file: File,
contentType: MediaType?,
private val channel: FileChannel,
private val chunkSize: Long = CHUNK_SIZE
) : FileRequestBody(file, contentType) {
private var offset: Long = 0
private var alreadyTransferred: Long = 0
private val buffer = ByteBuffer.allocate(4_096)
init {
require(chunkSize > 0) { "Chunk size must be greater than zero" }
}
override fun contentLength(): Long {
return chunkSize.coerceAtMost(channel.size() - channel.position())
}
override fun writeTo(sink: BufferedSink) {
var readCount: Int
var iterator: Iterator<OnDatatransferProgressListener>
try {
channel.position(offset)
val maxCount = (offset + chunkSize).coerceAtMost(channel.size())
while (channel.position() < maxCount) {
readCount = channel.read(buffer)
val bytesToWriteInBuffer = readCount.toLong().coerceAtMost(file.length() - alreadyTransferred).toInt()
sink.buffer.write(buffer.array(), 0, bytesToWriteInBuffer)
sink.flush()
buffer.clear()
if (alreadyTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks
alreadyTransferred += readCount.toLong()
}
synchronized(dataTransferListeners) {
iterator = dataTransferListeners.iterator()
while (iterator.hasNext()) {
iterator.next().onTransferProgress(readCount.toLong(), alreadyTransferred, file.length(), file.absolutePath)
}
}
}
} catch (exception: Exception) {
Timber.e(exception, "Transferred " + alreadyTransferred + " bytes from a total of " + file.length())
}
}
fun setOffset(newOffset: Long) {
offset = newOffset
}
}

View File

@ -1,117 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network
import android.content.ContentResolver
import android.net.Uri
import android.provider.OpenableColumns
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody
import okio.BufferedSink
import okio.Source
import okio.source
import timber.log.Timber
import java.io.IOException
class ContentUriRequestBody(
private val contentResolver: ContentResolver,
private val contentUri: Uri
) : RequestBody(), ProgressiveDataTransferer {
private val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
val fileSize: Long = contentResolver.query(contentUri, null, null, null, null)?.use { cursor ->
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
cursor.moveToFirst()
cursor.getLong(sizeIndex)
} ?: -1
override fun contentType(): MediaType? {
val contentType = contentResolver.getType(contentUri) ?: return null
return contentType.toMediaTypeOrNull()
}
override fun contentLength(): Long {
return fileSize
}
override fun writeTo(sink: BufferedSink) {
val inputStream = contentResolver.openInputStream(contentUri)
?: throw IOException("Couldn't open content URI for reading: $contentUri")
val previousTime = System.currentTimeMillis()
sink.writeAndUpdateProgress(inputStream.source())
inputStream.source().close()
val laterTime = System.currentTimeMillis()
Timber.d("Difference - ${laterTime - previousTime} milliseconds")
}
private fun BufferedSink.writeAndUpdateProgress(source: Source) {
var iterator: Iterator<OnDatatransferProgressListener>
try {
var totalBytesRead = 0L
var read: Long
while (source.read(this.buffer, BYTES_TO_READ).also { read = it } != -1L) {
totalBytesRead += read
this.flush()
synchronized(dataTransferListeners) {
iterator = dataTransferListeners.iterator()
while (iterator.hasNext()) {
iterator.next().onTransferProgress(read, totalBytesRead, fileSize, contentUri.toString())
}
}
}
} catch (e: Exception) {
Timber.e(e)
}
}
override fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
synchronized(dataTransferListeners) {
dataTransferListeners.add(listener)
}
}
override fun addDatatransferProgressListeners(listeners: MutableCollection<OnDatatransferProgressListener>) {
synchronized(dataTransferListeners) {
dataTransferListeners.addAll(listeners)
}
}
override fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener) {
synchronized(dataTransferListeners) {
dataTransferListeners.remove(listener)
}
}
companion object {
private const val BYTES_TO_READ = 4_096L
}
}

View File

@ -1,97 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network
import okhttp3.MediaType
import okhttp3.RequestBody
import okio.BufferedSink
import okio.Source
import okio.source
import timber.log.Timber
import java.io.File
import java.util.HashSet
/**
* A Request body that represents a file and include information about the progress when uploading it
*
* @author David González Verdugo
*/
open class FileRequestBody(
val file: File,
private val contentType: MediaType?,
) : RequestBody(), ProgressiveDataTransferer {
val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
override fun isOneShot(): Boolean = true
override fun contentType(): MediaType? = contentType
override fun contentLength(): Long = file.length()
override fun writeTo(sink: BufferedSink) {
val source: Source
var it: Iterator<OnDatatransferProgressListener>
try {
source = file.source()
var transferred: Long = 0
var read: Long
while (source.read(sink.buffer, BYTES_TO_READ).also { read = it } != -1L) {
transferred += read
sink.flush()
synchronized(dataTransferListeners) {
it = dataTransferListeners.iterator()
while (it.hasNext()) {
it.next().onTransferProgress(read, transferred, file.length(), file.absolutePath)
}
}
}
Timber.d("File with name ${file.name} and size ${file.length()} written in request body")
} catch (e: Exception) {
Timber.e(e)
}
}
override fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
synchronized(dataTransferListeners) {
dataTransferListeners.add(listener)
}
}
override fun addDatatransferProgressListeners(listeners: Collection<OnDatatransferProgressListener>) {
synchronized(dataTransferListeners) {
dataTransferListeners.addAll(listeners)
}
}
override fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener) {
synchronized(dataTransferListeners) {
dataTransferListeners.remove(listener)
}
}
companion object {
private const val BYTES_TO_READ = 4_096L
}
}

View File

@ -1,97 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import android.content.Context;
import timber.log.Timber;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
public class NetworkUtils {
private static String LOCAL_TRUSTSTORE_FILENAME = "knownServers.bks";
private static String LOCAL_TRUSTSTORE_PASSWORD = "password";
private static KeyStore mKnownServersStore = null;
/**
* Returns the local store of reliable server certificates, explicitly accepted by the user.
* <p>
* Returns a KeyStore instance with empty content if the local store was never created.
* <p>
* Loads the store from the storage environment if needed.
*
* @param context Android context where the operation is being performed.
* @return KeyStore instance with explicitly-accepted server certificates.
* @throws KeyStoreException When the KeyStore instance could not be created.
* @throws IOException When an existing local trust store could not be loaded.
* @throws NoSuchAlgorithmException When the existing local trust store was saved with an unsupported algorithm.
* @throws CertificateException When an exception occurred while loading the certificates from the local
* trust store.
*/
public static KeyStore getKnownServersStore(Context context)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
if (mKnownServersStore == null) {
//mKnownServersStore = KeyStore.getInstance("BKS");
mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType());
File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME);
Timber.d("Searching known-servers store at %s", localTrustStoreFile.getAbsolutePath());
if (localTrustStoreFile.exists()) {
InputStream in = new FileInputStream(localTrustStoreFile);
try {
mKnownServersStore.load(in, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
} finally {
in.close();
}
} else {
// next is necessary to initialize an empty KeyStore instance
mKnownServersStore.load(null, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
}
}
return mKnownServersStore;
}
public static void addCertToKnownServersStore(Certificate cert, Context context)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
KeyStore knownServers = getKnownServersStore(context);
knownServers.setCertificateEntry(Integer.toString(cert.hashCode()), cert);
try (FileOutputStream fos = context.openFileOutput(LOCAL_TRUSTSTORE_FILENAME, Context.MODE_PRIVATE)) {
knownServers.store(fos, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
}
}
}

View File

@ -1,123 +0,0 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
*
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import com.owncloud.android.lib.common.http.HttpConstants;
import java.util.Arrays;
/**
* Aggregate saving the list of URLs followed in a sequence of redirections during the exceution of a
* {@link RemoteOperation}, and the status codes corresponding to all
* of them.
* <p>
* The last status code saved corresponds to the first response not being a redirection, unless the sequence exceeds
* the maximum length of redirections allowed by the {@link com.owncloud.android.lib.common.OwnCloudClient} instance
* that ran the operation.
* <p>
* If no redirection was followed, the last (and first) status code contained corresponds to the original URL in the
* request.
*/
public class RedirectionPath {
private int[] mStatuses = null;
private int mLastStatus = -1;
private String[] mLocations = null;
private int mLastLocation = -1;
/**
* Public constructor.
*
* @param status Status code resulting of executing a request on the original URL.
* @param maxRedirections Maximum number of redirections that will be contained.
* @throws IllegalArgumentException If 'maxRedirections' is < 0
*/
public RedirectionPath(int status, int maxRedirections) {
if (maxRedirections < 0) {
throw new IllegalArgumentException("maxRedirections MUST BE zero or greater");
}
mStatuses = new int[maxRedirections + 1];
Arrays.fill(mStatuses, -1);
mStatuses[++mLastStatus] = status;
}
/**
* Adds a new location URL to the list of followed redirections.
*
* @param location URL extracted from a 'Location' header in a redirection.
*/
public void addLocation(String location) {
if (mLocations == null) {
mLocations = new String[mStatuses.length - 1];
}
if (mLastLocation < mLocations.length - 1) {
mLocations[++mLastLocation] = location;
}
}
/**
* Adds a new status code to the list of status corresponding to followed redirections.
*
* @param status Status code from the response of another followed redirection.
*/
public void addStatus(int status) {
if (mLastStatus < mStatuses.length - 1) {
mStatuses[++mLastStatus] = status;
}
}
/**
* @return Last status code saved.
*/
public int getLastStatus() {
return mStatuses[mLastStatus];
}
/**
* @return Last location followed corresponding to a permanent redirection (status code 301).
*/
public String getLastPermanentLocation() {
for (int i = mLastStatus; i >= 0; i--) {
if (mStatuses[i] == HttpConstants.HTTP_MOVED_PERMANENTLY && i <= mLastLocation) {
return mLocations[i];
}
}
return null;
}
/**
* @return Count of locations.
*/
public int getRedirectionsCount() {
return mLastLocation + 1;
}
}

View File

@ -1,117 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
* Copyright (C) 2012 Bartek Przybylski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import android.net.Uri;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class WebdavUtils {
private static final SimpleDateFormat[] DATETIME_FORMATS = {
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US),
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'", Locale.US),
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US),
new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US),
new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US),
new SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.US)
};
public static Date parseResponseDate(String date) {
Date returnDate;
SimpleDateFormat format;
for (SimpleDateFormat datetimeFormat : DATETIME_FORMATS) {
try {
format = datetimeFormat;
synchronized (format) {
returnDate = format.parse(date);
}
return returnDate;
} catch (ParseException e) {
// this is not the format
}
}
return null;
}
/**
* Encodes a path according to URI RFC 2396.
* <p>
* If the received path doesn't start with "/", the method adds it.
*
* @param remoteFilePath Path
* @return Encoded path according to RFC 2396, always starting with "/"
*/
public static String encodePath(String remoteFilePath) {
String encodedPath = Uri.encode(remoteFilePath, "/");
if (!encodedPath.startsWith("/")) {
encodedPath = "/" + encodedPath;
}
return encodedPath;
}
/**
* @param httpBaseMethod from which to get the etag
* @return etag from response
*/
public static String getEtagFromResponse(HttpBaseMethod httpBaseMethod) {
String eTag = httpBaseMethod.getResponseHeader("OC-ETag");
if (eTag == null) {
eTag = httpBaseMethod.getResponseHeader("oc-etag");
}
if (eTag == null) {
eTag = httpBaseMethod.getResponseHeader("ETag");
}
if (eTag == null) {
eTag = httpBaseMethod.getResponseHeader("etag");
}
String result = "";
if (eTag != null) {
result = eTag;
}
return result;
}
public static String normalizeProtocolPrefix(String url, boolean isSslConn) {
if (!url.toLowerCase().startsWith("http://") &&
!url.toLowerCase().startsWith("https://")) {
if (isSslConn) {
return "https://" + url;
} else {
return "http://" + url;
}
}
return url;
}
}

View File

@ -1,143 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.operations;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
/**
* Parser for server exceptions
*
* @author davidgonzalez
*/
public class ErrorMessageParser {
// No namespaces
private static final String ns = null;
// Nodes for XML Parser
private static final String NODE_ERROR = "d:error";
private static final String NODE_MESSAGE = "s:message";
/**
* Parse exception response
*
* @param is
* @return errorMessage for an exception
* @throws XmlPullParserException
* @throws IOException
*/
public String parseXMLResponse(InputStream is) throws XmlPullParserException,
IOException {
String errorMessage = "";
try {
// XMLPullParser
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(is, null);
parser.nextTag();
errorMessage = readError(parser);
} finally {
is.close();
}
return errorMessage;
}
/**
* Parse OCS node
*
* @param parser
* @return reason for exception
* @throws XmlPullParserException
* @throws IOException
*/
private String readError(XmlPullParser parser) throws XmlPullParserException, IOException {
String errorMessage = "";
parser.require(XmlPullParser.START_TAG, ns, NODE_ERROR);
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
// read NODE_MESSAGE
if (name.equalsIgnoreCase(NODE_MESSAGE)) {
errorMessage = readText(parser);
} else {
skip(parser);
}
}
return errorMessage;
}
/**
* Skip tags in parser procedure
*
* @param parser
* @throws XmlPullParserException
* @throws IOException
*/
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
if (parser.getEventType() != XmlPullParser.START_TAG) {
throw new IllegalStateException();
}
int depth = 1;
while (depth != 0) {
switch (parser.next()) {
case XmlPullParser.END_TAG:
depth--;
break;
case XmlPullParser.START_TAG:
depth++;
break;
}
}
}
/**
* Read the text from a node
*
* @param parser
* @return Text of the node
* @throws IOException
* @throws XmlPullParserException
*/
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
String result = "";
if (parser.next() == XmlPullParser.TEXT) {
result = parser.getText();
parser.nextTag();
}
return result;
}
}

View File

@ -1,150 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.operations;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
/**
* Parser for Invalid Character server exception
*
* @author masensio
*/
public class InvalidCharacterExceptionParser {
private static final String EXCEPTION_STRING = "OC\\Connector\\Sabre\\Exception\\InvalidPath";
private static final String EXCEPTION_UPLOAD_STRING = "OCP\\Files\\InvalidPathException";
// No namespaces
private static final String ns = null;
// Nodes for XML Parser
private static final String NODE_ERROR = "d:error";
private static final String NODE_EXCEPTION = "s:exception";
/**
* Parse is as an Invalid Path Exception
*
* @param is
* @return if The exception is an Invalid Char Exception
* @throws XmlPullParserException
* @throws IOException
*/
public boolean parseXMLResponse(InputStream is) throws XmlPullParserException,
IOException {
boolean result = false;
try {
// XMLPullParser
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(is, null);
parser.nextTag();
result = readError(parser);
} finally {
is.close();
}
return result;
}
/**
* Parse OCS node
*
* @param parser
* @return List of ShareRemoteFiles
* @throws XmlPullParserException
* @throws IOException
*/
private boolean readError(XmlPullParser parser) throws XmlPullParserException, IOException {
String exception = "";
parser.require(XmlPullParser.START_TAG, ns, NODE_ERROR);
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
// read NODE_EXCEPTION
if (name.equalsIgnoreCase(NODE_EXCEPTION)) {
exception = readText(parser);
} else {
skip(parser);
}
}
return exception.equalsIgnoreCase(EXCEPTION_STRING) ||
exception.equalsIgnoreCase(EXCEPTION_UPLOAD_STRING);
}
/**
* Skip tags in parser procedure
*
* @param parser
* @throws XmlPullParserException
* @throws IOException
*/
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
if (parser.getEventType() != XmlPullParser.START_TAG) {
throw new IllegalStateException();
}
int depth = 1;
while (depth != 0) {
switch (parser.next()) {
case XmlPullParser.END_TAG:
depth--;
break;
case XmlPullParser.START_TAG:
depth++;
break;
}
}
}
/**
* Read the text from a node
*
* @param parser
* @return Text of the node
* @throws IOException
* @throws XmlPullParserException
*/
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
String result = "";
if (parser.next() == XmlPullParser.TEXT) {
result = parser.getText();
parser.nextTag();
}
return result;
}
}

View File

@ -1,292 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.operations;
import android.accounts.Account;
import android.accounts.AccountsException;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.os.Handler;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.SingleSessionManager;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import okhttp3.OkHttpClient;
import timber.log.Timber;
import java.io.IOException;
@SuppressWarnings("WeakerAccess")
public abstract class RemoteOperation<T> implements Runnable {
/**
* OCS API header name
*/
public static final String OCS_API_HEADER = "OCS-APIREQUEST";
/**
* OCS API header value
*/
public static final String OCS_API_HEADER_VALUE = "true";
/**
* ownCloud account in the remote ownCloud server to operate
*/
protected Account mAccount = null;
/**
* Android Application context
*/
protected Context mContext = null;
/**
* Object to interact with the remote server
*/
private OwnCloudClient mClient = null;
/**
* Object to interact with the remote server
*/
private OkHttpClient mHttpClient = null;
/**
* Callback object to notify about the execution of the remote operation
*/
private OnRemoteOperationListener mListener = null;
/**
* Handler to the thread where mListener methods will be called
*/
private Handler mListenerHandler = null;
/**
* Asynchronously executes the remote operation
* <p>
* This method should be used whenever an ownCloud account is available,
* instead of {@link #execute(OwnCloudClient, OnRemoteOperationListener, Handler))}.
*
* @param account ownCloud account in remote ownCloud server to reach during the
* execution of the operation.
* @param context Android context for the component calling the method.
* @param listener Listener to be notified about the execution of the operation.
* @param listenerHandler Handler associated to the thread where the methods of the listener
* objects must be called.
* @return Thread were the remote operation is executed.
*/
public Thread execute(Account account, Context context,
OnRemoteOperationListener listener, Handler listenerHandler) {
if (account == null) {
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
}
if (context == null) {
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
}
// mAccount and mContext in the runnerThread to create below
mAccount = account;
mContext = context.getApplicationContext();
mClient = null; // the client instance will be created from
mListener = listener;
mListenerHandler = listenerHandler;
Thread runnerThread = new Thread(this);
runnerThread.start();
return runnerThread;
}
/**
* Asynchronously executes the remote operation
*
* @param client Client object to reach an ownCloud server
* during the execution of the operation.
* @param listener Listener to be notified about the execution of the operation.
* @param listenerHandler Handler, if passed in, associated to the thread where the methods of
* the listener objects must be called.
* @return Thread were the remote operation is executed.
*/
public Thread execute(OwnCloudClient client, OnRemoteOperationListener listener, Handler listenerHandler) {
if (client == null) {
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL OwnCloudClient");
}
mClient = client;
if (client.getAccount() != null) {
mAccount = client.getAccount().getSavedAccount();
}
mContext = client.getContext();
if (listener == null) {
throw new IllegalArgumentException
("Trying to execute a remote operation asynchronously without a listener to notify the result");
}
mListener = listener;
if (listenerHandler != null) {
mListenerHandler = listenerHandler;
}
Thread runnerThread = new Thread(this);
runnerThread.start();
return runnerThread;
}
private void grantOwnCloudClient() throws
AccountUtils.AccountNotFoundException, OperationCanceledException, AuthenticatorException, IOException {
if (mClient == null) {
if (mAccount != null && mContext != null) {
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
mClient = SingleSessionManager.getDefaultSingleton().
getClientFor(ocAccount, mContext, SingleSessionManager.getConnectionValidator());
} else {
throw new IllegalStateException("Trying to run a remote operation " +
"asynchronously with no client and no chance to create one (no account)");
}
}
}
/**
* Returns the current client instance to access the remote server.
*
* @return Current client instance to access the remote server.
*/
public final OwnCloudClient getClient() {
return mClient;
}
/**
* Abstract method to implement the operation in derived classes.
*/
protected abstract RemoteOperationResult<T> run(OwnCloudClient client);
/**
* Synchronously executes the remote operation on the received ownCloud account.
* <p>
* Do not call this method from the main thread.
* <p>
* This method should be used whenever an ownCloud account is available, instead of
* {@link #execute(OwnCloudClient)}.
*
* @param account ownCloud account in remote ownCloud server to reach during the
* execution of the operation.
* @param context Android context for the component calling the method.
* @return Result of the operation.
*/
public RemoteOperationResult<T> execute(Account account, Context context) {
if (account == null) {
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
}
if (context == null) {
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
}
mAccount = account;
mContext = context.getApplicationContext();
return runOperation();
}
/**
* Synchronously executes the remote operation
* <p>
* Do not call this method from the main thread.
*
* @param client Client object to reach an ownCloud server during the execution of
* the operation.
* @return Result of the operation.
*/
public RemoteOperationResult<T> execute(OwnCloudClient client) {
if (client == null) {
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL OwnCloudClient");
}
mClient = client;
if (client.getAccount() != null) {
mAccount = client.getAccount().getSavedAccount();
}
mContext = client.getContext();
return runOperation();
}
/**
* Synchronously executes the remote operation
* <p>
* Do not call this method from the main thread.
*
* @param client Client object to reach an ownCloud server during the execution of
* the operation.
* @return Result of the operation.
*/
public RemoteOperationResult<T> execute(OkHttpClient client, Context context) {
if (client == null) {
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL OwnCloudClient");
}
mHttpClient = client;
mContext = context;
return runOperation();
}
/**
* Run operation for asynchronous or synchronous 'onExecute' method.
* <p>
* Considers and performs silent refresh of account credentials if possible
*
* @return Remote operation result
*/
private RemoteOperationResult<T> runOperation() {
RemoteOperationResult<T> result;
try {
grantOwnCloudClient();
result = run(mClient);
} catch (AccountsException | IOException e) {
Timber.e(e, "Error while trying to access to %s", mAccount.name);
result = new RemoteOperationResult<>(e);
}
return result;
}
/**
* Asynchronous execution of the operation
* started by {@link RemoteOperation#execute(OwnCloudClient,
* OnRemoteOperationListener, Handler)},
* and result posting.
*/
@Override
public final void run() {
final RemoteOperationResult resultToSend = runOperation();
if (mListenerHandler != null && mListener != null) {
mListenerHandler.post(() ->
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend));
} else if (mListener != null) {
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
}
}
}

View File

@ -1,598 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.operations;
import android.accounts.Account;
import android.accounts.AccountsException;
import at.bitfire.dav4jvm.exception.DavException;
import at.bitfire.dav4jvm.exception.HttpException;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import com.owncloud.android.lib.common.network.CertificateCombinedException;
import okhttp3.Headers;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.json.JSONException;
import timber.log.Timber;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class RemoteOperationResult<T>
implements Serializable {
/**
* Generated - should be refreshed every time the class changes!!
*/
private static final long serialVersionUID = 4968939884332372230L;
private static final String LOCATION = "location";
private static final String WWW_AUTHENTICATE = "www-authenticate";
private boolean mSuccess = false;
private int mHttpCode = -1;
private String mHttpPhrase = null;
private Exception mException = null;
private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
private String mRedirectedLocation = "";
private List<String> mAuthenticate = new ArrayList<>();
private String mLastPermanentLocation = null;
private T mData = null;
/**
* Public constructor from result code.
* <p>
* To be used when the caller takes the responsibility of interpreting the result of a {@link RemoteOperation}
*
* @param code {@link ResultCode} decided by the caller.
*/
public RemoteOperationResult(ResultCode code) {
mCode = code;
mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL ||
code == ResultCode.OK_NO_SSL ||
code == ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION);
}
/**
* Create a new RemoteOperationResult based on the result given by a previous one.
* It does not copy the data.
*
* @param prevRemoteOperation
*/
public RemoteOperationResult(RemoteOperationResult prevRemoteOperation) {
mCode = prevRemoteOperation.mCode;
mHttpCode = prevRemoteOperation.mHttpCode;
mHttpPhrase = prevRemoteOperation.mHttpPhrase;
mAuthenticate = prevRemoteOperation.mAuthenticate;
mException = prevRemoteOperation.mException;
mLastPermanentLocation = prevRemoteOperation.mLastPermanentLocation;
mSuccess = prevRemoteOperation.mSuccess;
mRedirectedLocation = prevRemoteOperation.mRedirectedLocation;
}
/**
* Public constructor from exception.
* <p>
* To be used when an exception prevented the end of the {@link RemoteOperation}.
* <p>
* Determines a {@link ResultCode} depending on the type of the exception.
*
* @param e Exception that interrupted the {@link RemoteOperation}
*/
public RemoteOperationResult(Exception e) {
mException = e;
//TODO: Do propper exception handling and remove this
Timber.e("---------------------------------" +
"\nCreate RemoteOperationResult from exception." +
"\n Message: %s" +
"\n Stacktrace: %s" +
"\n---------------------------------",
ExceptionUtils.getMessage(e),
ExceptionUtils.getStackTrace(e));
if (e instanceof OperationCancelledException) {
mCode = ResultCode.CANCELLED;
} else if (e instanceof SocketException) {
mCode = ResultCode.WRONG_CONNECTION;
} else if (e instanceof SocketTimeoutException) {
mCode = ResultCode.TIMEOUT;
} else if (e instanceof MalformedURLException) {
mCode = ResultCode.INCORRECT_ADDRESS;
} else if (e instanceof UnknownHostException) {
mCode = ResultCode.HOST_NOT_AVAILABLE;
} else if (e instanceof AccountUtils.AccountNotFoundException) {
mCode = ResultCode.ACCOUNT_NOT_FOUND;
} else if (e instanceof AccountsException) {
mCode = ResultCode.ACCOUNT_EXCEPTION;
} else if (e instanceof SSLException || e instanceof RuntimeException) {
if (e instanceof SSLPeerUnverifiedException) {
mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
} else {
CertificateCombinedException se = getCertificateCombinedException(e);
if (se != null) {
mException = se;
if (se.isRecoverable()) {
mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
}
} else if (e instanceof RuntimeException) {
mCode = ResultCode.HOST_NOT_AVAILABLE;
} else {
mCode = ResultCode.SSL_ERROR;
}
}
} else if (e instanceof FileNotFoundException) {
mCode = ResultCode.LOCAL_FILE_NOT_FOUND;
} else if (e instanceof ProtocolException) {
mCode = ResultCode.NETWORK_ERROR;
}
else {
mCode = ResultCode.UNKNOWN_ERROR;
}
}
/**
* Public constructor from separate elements of an HTTP or DAV response.
* <p>
* To be used when the result needs to be interpreted from the response of an HTTP/DAV method.
* <p>
* Determines a {@link ResultCode} from the already executed method received as a parameter. Generally,
* will depend on the HTTP code and HTTP response headers received. In some cases will inspect also the
* response body
*
* @param httpMethod
* @throws IOException
*/
public RemoteOperationResult(HttpBaseMethod httpMethod) throws IOException {
this(httpMethod.getStatusCode(),
httpMethod.getStatusMessage(),
httpMethod.getResponseHeaders()
);
if (mHttpCode == HttpConstants.HTTP_BAD_REQUEST) { // 400
String bodyResponse = httpMethod.getResponseBodyAsString();
// do not get for other HTTP codes!; could not be available
if (bodyResponse != null && bodyResponse.length() > 0) {
InputStream is = new ByteArrayInputStream(bodyResponse.getBytes());
InvalidCharacterExceptionParser xmlParser = new InvalidCharacterExceptionParser();
try {
if (xmlParser.parseXMLResponse(is)) {
mCode = ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER;
} else {
parseErrorMessageAndSetCode(
httpMethod.getResponseBodyAsString(),
ResultCode.SPECIFIC_BAD_REQUEST
);
}
} catch (Exception e) {
Timber.w("Error reading exception from server: %s", e.getMessage());
// mCode stays as set in this(success, httpCode, headers)
}
}
}
// before
switch (mHttpCode) {
case HttpConstants.HTTP_FORBIDDEN:
parseErrorMessageAndSetCode(
httpMethod.getResponseBodyAsString(),
ResultCode.SPECIFIC_FORBIDDEN
);
break;
case HttpConstants.HTTP_UNSUPPORTED_MEDIA_TYPE:
parseErrorMessageAndSetCode(
httpMethod.getResponseBodyAsString(),
ResultCode.SPECIFIC_UNSUPPORTED_MEDIA_TYPE
);
break;
case HttpConstants.HTTP_SERVICE_UNAVAILABLE:
parseErrorMessageAndSetCode(
httpMethod.getResponseBodyAsString(),
ResultCode.SPECIFIC_SERVICE_UNAVAILABLE
);
break;
case HttpConstants.HTTP_METHOD_NOT_ALLOWED:
parseErrorMessageAndSetCode(
httpMethod.getResponseBodyAsString(),
ResultCode.SPECIFIC_METHOD_NOT_ALLOWED
);
break;
case HttpConstants.HTTP_TOO_EARLY:
mCode = ResultCode.TOO_EARLY;
break;
default:
break;
}
}
/**
* Public constructor from separate elements of an HTTP or DAV response.
* <p>
* To be used when the result needs to be interpreted from HTTP response elements that could come from
* different requests (WARNING: black magic, try to avoid).
* <p>
* <p>
* Determines a {@link ResultCode} depending on the HTTP code and HTTP response headers received.
*
* @param httpCode HTTP status code returned by an HTTP/DAV method.
* @param httpPhrase HTTP status line phrase returned by an HTTP/DAV method
* @param headers HTTP response header returned by an HTTP/DAV method
*/
public RemoteOperationResult(int httpCode, String httpPhrase, Headers headers) {
this(httpCode, httpPhrase);
if (headers != null) {
for (Map.Entry<String, List<String>> header : headers.toMultimap().entrySet()) {
if (LOCATION.equalsIgnoreCase(header.getKey())) {
mRedirectedLocation = header.getValue().get(0);
continue;
}
if (WWW_AUTHENTICATE.equalsIgnoreCase(header.getKey())) {
for (String value: header.getValue()) {
mAuthenticate.add(value.toLowerCase());
}
}
}
}
}
/**
* Private constructor for results built interpreting a HTTP or DAV response.
* <p>
* Determines a {@link ResultCode} depending of the type of the exception.
*
* @param httpCode HTTP status code returned by the HTTP/DAV method.
* @param httpPhrase HTTP status line phrase returned by the HTTP/DAV method
*/
private RemoteOperationResult(int httpCode, String httpPhrase) {
mHttpCode = httpCode;
mHttpPhrase = httpPhrase;
if (httpCode > 0) {
switch (httpCode) {
case HttpConstants.HTTP_UNAUTHORIZED: // 401
mCode = ResultCode.UNAUTHORIZED;
break;
case HttpConstants.HTTP_FORBIDDEN: // 403
mCode = ResultCode.FORBIDDEN;
break;
case HttpConstants.HTTP_NOT_FOUND: // 404
mCode = ResultCode.FILE_NOT_FOUND;
break;
case HttpConstants.HTTP_CONFLICT: // 409
mCode = ResultCode.CONFLICT;
break;
case HttpConstants.HTTP_INTERNAL_SERVER_ERROR: // 500
mCode = ResultCode.INSTANCE_NOT_CONFIGURED; // assuming too much...
break;
case HttpConstants.HTTP_SERVICE_UNAVAILABLE: // 503
mCode = ResultCode.SERVICE_UNAVAILABLE;
break;
case HttpConstants.HTTP_INSUFFICIENT_STORAGE: // 507
mCode = ResultCode.QUOTA_EXCEEDED; // surprise!
break;
default:
mCode = ResultCode.UNHANDLED_HTTP_CODE; // UNKNOWN ERROR
Timber.d("RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + mHttpCode + " " + mHttpPhrase);
}
}
}
/**
* Parse the error message included in the body response, if any, and set the specific result
* code
*
* @param bodyResponse okHttp response body
* @param resultCode our own custom result code
*/
private void parseErrorMessageAndSetCode(String bodyResponse, ResultCode resultCode) {
if (bodyResponse != null && bodyResponse.length() > 0) {
InputStream is = new ByteArrayInputStream(bodyResponse.getBytes());
ErrorMessageParser xmlParser = new ErrorMessageParser();
try {
String errorMessage = xmlParser.parseXMLResponse(is);
if (!errorMessage.equals("")) {
mCode = resultCode;
mHttpPhrase = errorMessage;
}
} catch (Exception e) {
Timber.w("Error reading exception from server: %s\nTrace: %s", e.getMessage(), ExceptionUtils.getStackTrace(e));
// mCode stays as set in this(success, httpCode, headers)
}
}
}
public boolean isSuccess() {
return mSuccess;
}
public void setSuccess(boolean success) {
this.mSuccess = success;
}
public boolean isCancelled() {
return mCode == ResultCode.CANCELLED;
}
public int getHttpCode() {
return mHttpCode;
}
public String getHttpPhrase() {
return mHttpPhrase;
}
public ResultCode getCode() {
return mCode;
}
public Exception getException() {
return mException;
}
public boolean isSslRecoverableException() {
return mCode == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
}
public boolean isRedirectToNonSecureConnection() {
return mCode == ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION;
}
private CertificateCombinedException getCertificateCombinedException(Exception e) {
CertificateCombinedException result = null;
if (e instanceof CertificateCombinedException) {
return (CertificateCombinedException) e;
}
Throwable cause = mException.getCause();
Throwable previousCause = null;
while (cause != null && cause != previousCause &&
!(cause instanceof CertificateCombinedException)) {
previousCause = cause;
cause = cause.getCause();
}
if (cause instanceof CertificateCombinedException) {
result = (CertificateCombinedException) cause;
}
return result;
}
public String getLogMessage() {
if (mException != null) {
if (mException instanceof OperationCancelledException) {
return "Operation cancelled by the caller";
} else if (mException instanceof SocketException) {
return "Socket exception";
} else if (mException instanceof SocketTimeoutException) {
return "Socket timeout exception";
} else if (mException instanceof MalformedURLException) {
return "Malformed URL exception";
} else if (mException instanceof UnknownHostException) {
return "Unknown host exception";
} else if (mException instanceof CertificateCombinedException) {
if (((CertificateCombinedException) mException).isRecoverable()) {
return "SSL recoverable exception";
} else {
return "SSL exception";
}
} else if (mException instanceof SSLException) {
return "SSL exception";
} else if (mException instanceof DavException) {
return "Unexpected WebDAV exception";
} else if (mException instanceof HttpException) {
return "HTTP violation";
} else if (mException instanceof IOException) {
return "Unrecovered transport exception";
} else if (mException instanceof AccountUtils.AccountNotFoundException) {
Account failedAccount =
((AccountUtils.AccountNotFoundException) mException).getFailedAccount();
return mException.getMessage() + " (" +
(failedAccount != null ? failedAccount.name : "NULL") + ")";
} else if (mException instanceof AccountsException) {
return "Exception while using account";
} else if (mException instanceof JSONException) {
return "JSON exception";
} else {
return "Unexpected exception";
}
}
if (mCode == ResultCode.INSTANCE_NOT_CONFIGURED) {
return "The ownCloud server is not configured!";
} else if (mCode == ResultCode.NO_NETWORK_CONNECTION) {
return "No network connection";
} else if (mCode == ResultCode.BAD_OC_VERSION) {
return "No valid ownCloud version was found at the server";
} else if (mCode == ResultCode.LOCAL_STORAGE_FULL) {
return "Local storage full";
} else if (mCode == ResultCode.LOCAL_STORAGE_NOT_MOVED) {
return "Error while moving file to final directory";
} else if (mCode == ResultCode.ACCOUNT_NOT_NEW) {
return "Account already existing when creating a new one";
} else if (mCode == ResultCode.ACCOUNT_NOT_THE_SAME) {
return "Authenticated with a different account than the one updating";
} else if (mCode == ResultCode.INVALID_CHARACTER_IN_NAME) {
return "The file name contains an forbidden character";
} else if (mCode == ResultCode.FILE_NOT_FOUND) {
return "Local file does not exist";
} else if (mCode == ResultCode.SYNC_CONFLICT) {
return "Synchronization conflict";
}
return "Operation finished with HTTP status code " + mHttpCode + " (" +
(isSuccess() ? "success" : "fail") + ")";
}
public boolean isServerFail() {
return (mHttpCode >= HttpConstants.HTTP_INTERNAL_SERVER_ERROR);
}
public boolean isException() {
return (mException != null);
}
public boolean isTemporalRedirection() {
return (mHttpCode == 302 || mHttpCode == 307);
}
public String getRedirectedLocation() {
return mRedirectedLocation;
}
/**
* Checks if is a non https connection
*
* @return boolean true/false
*/
public boolean isNonSecureRedirection() {
return (mRedirectedLocation != null && !(mRedirectedLocation.toLowerCase().startsWith("https://")));
}
public List<String> getAuthenticateHeaders() {
return mAuthenticate;
}
public String getLastPermanentLocation() {
return mLastPermanentLocation;
}
public void setLastPermanentLocation(String lastPermanentLocation) {
mLastPermanentLocation = lastPermanentLocation;
}
public T getData() {
return mData;
}
public void setData(T data) {
mData = data;
}
public void setHttpPhrase(String httpPhrase) {
mHttpPhrase = httpPhrase;
}
public enum ResultCode {
OK,
OK_SSL,
OK_NO_SSL,
UNHANDLED_HTTP_CODE,
UNAUTHORIZED,
FILE_NOT_FOUND,
INSTANCE_NOT_CONFIGURED,
UNKNOWN_ERROR,
WRONG_CONNECTION,
TIMEOUT,
INCORRECT_ADDRESS,
HOST_NOT_AVAILABLE,
NO_NETWORK_CONNECTION,
SSL_ERROR,
SSL_RECOVERABLE_PEER_UNVERIFIED,
BAD_OC_VERSION,
CANCELLED,
INVALID_LOCAL_FILE_NAME,
INVALID_OVERWRITE,
CONFLICT,
OAUTH2_ERROR,
SYNC_CONFLICT,
LOCAL_STORAGE_FULL,
LOCAL_STORAGE_NOT_MOVED,
LOCAL_STORAGE_NOT_COPIED,
OAUTH2_ERROR_ACCESS_DENIED,
QUOTA_EXCEEDED,
ACCOUNT_NOT_FOUND,
ACCOUNT_EXCEPTION,
ACCOUNT_NOT_NEW,
ACCOUNT_NOT_THE_SAME,
INVALID_CHARACTER_IN_NAME,
SHARE_NOT_FOUND,
LOCAL_STORAGE_NOT_REMOVED,
FORBIDDEN,
SHARE_FORBIDDEN,
SPECIFIC_FORBIDDEN,
OK_REDIRECT_TO_NON_SECURE_CONNECTION,
INVALID_MOVE_INTO_DESCENDANT,
INVALID_COPY_INTO_DESCENDANT,
PARTIAL_MOVE_DONE,
PARTIAL_COPY_DONE,
SHARE_WRONG_PARAMETER,
WRONG_SERVER_RESPONSE,
INVALID_CHARACTER_DETECT_IN_SERVER,
DELAYED_FOR_WIFI,
LOCAL_FILE_NOT_FOUND,
SERVICE_UNAVAILABLE,
SPECIFIC_SERVICE_UNAVAILABLE,
SPECIFIC_UNSUPPORTED_MEDIA_TYPE,
SPECIFIC_METHOD_NOT_ALLOWED,
SPECIFIC_BAD_REQUEST,
TOO_EARLY,
NETWORK_ERROR,
}
}

View File

@ -1,29 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.utils
fun Any.isOneOf(vararg values: Any): Boolean {
return this in values
}

View File

@ -1,70 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.utils;
import java.util.Random;
import java.util.UUID;
/**
* Class with methods to generate random values
*
* @author David González Verdugo
*/
public class RandomUtils {
private static final String CANDIDATECHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"1234567890-+/_=.:";
/**
* @param length the number of random chars to be generated
* @return String containing random chars
*/
public static String generateRandomString(int length) {
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(CANDIDATECHARS.charAt(random.nextInt(CANDIDATECHARS.length())));
}
return sb.toString();
}
/**
* @param min minimum integer to obtain randomly
* @param max maximum integer to obtain randomly
* @return random integer between min and max
*/
public static int generateRandomInteger(int min, int max) {
Random r = new Random();
return r.nextInt(max - min) + min;
}
/**
* @return random UUID
*/
public static String generateRandomUUID() {
return UUID.randomUUID().toString();
}
}

View File

@ -1,52 +0,0 @@
/* ownCloud Android Library is available under MIT license
*
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.owncloud.android.lib.resources
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
// Response retrieved by OCS Rest API, used to obtain capabilities, shares and user info among others.
// More info: https://doc.owncloud.com/server/developer_manual/core/apis/ocs-capabilities.html
@JsonClass(generateAdapter = true)
data class CommonOcsResponse<T>(
val ocs: OCSResponse<T>
)
@JsonClass(generateAdapter = true)
data class OCSResponse<T>(
val meta: MetaData,
val data: T?
)
@JsonClass(generateAdapter = true)
data class MetaData(
val status: String,
@Json(name = "statuscode")
val statusCode: Int,
val message: String?,
@Json(name = "itemsperpage")
val itemsPerPage: String?,
@Json(name = "totalitems")
val totalItems: String?
)

View File

@ -1,108 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.appregistry
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.nonwebdav.PostMethod
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.squareup.moshi.Json
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import okhttp3.FormBody
import okhttp3.RequestBody
import timber.log.Timber
import java.net.URL
import java.util.concurrent.TimeUnit
class CreateRemoteFileWithAppProviderOperation(
private val createFileWithAppProviderEndpoint: String,
private val parentContainerId: String,
private val filename: String,
) : RemoteOperation<String>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
return try {
val createFileWithAppProviderRequestBody = CreateFileWithAppProviderParams(parentContainerId, filename)
.toRequestBody()
val stringUrl = client.baseUri.toString() + WebdavUtils.encodePath(createFileWithAppProviderEndpoint)
val postMethod = PostMethod(URL(stringUrl), createFileWithAppProviderRequestBody).apply {
setReadTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
setConnectionTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
}
val status = client.executeHttpMethod(postMethod)
Timber.d("Create file $filename with app provider in folder with ID $parentContainerId - $status${if (!isSuccess(status)) "(FAIL)" else ""}")
if (isSuccess(status)) RemoteOperationResult<String>(ResultCode.OK).apply {
val moshi = Moshi.Builder().build()
val adapter: JsonAdapter<CreateFileWithAppProviderResponse> = moshi.adapter(CreateFileWithAppProviderResponse::class.java)
data = postMethod.getResponseBodyAsString()?.let { adapter.fromJson(it)!!.fileId }
}
else RemoteOperationResult<String>(postMethod).apply { data = "" }
} catch (e: Exception) {
val result = RemoteOperationResult<String>(e)
Timber.e(e, "Create file $filename with app provider in folder with ID $parentContainerId failed")
result
}
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
data class CreateFileWithAppProviderParams(
val parentContainerId: String,
val filename: String,
) {
fun toRequestBody(): RequestBody =
FormBody.Builder()
.add(PARAM_PARENT_CONTAINER_ID, parentContainerId)
.add(PARAM_FILENAME, filename)
.build()
companion object {
const val PARAM_PARENT_CONTAINER_ID = "parent_container_id"
const val PARAM_FILENAME = "filename"
}
}
@JsonClass(generateAdapter = true)
data class CreateFileWithAppProviderResponse(
@Json(name = "file_id")
val fileId: String,
)
companion object {
private const val TIMEOUT: Long = 5_000
}
}

View File

@ -1,78 +0,0 @@
/* ownCloud Android Library is available under MIT license
* @author Abel García de Prada
*
* Copyright (C) 2023 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.appregistry
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK
import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import timber.log.Timber
import java.net.URL
class GetRemoteAppRegistryOperation(private val appUrl: String?) : RemoteOperation<AppRegistryResponse>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<AppRegistryResponse> {
var result: RemoteOperationResult<AppRegistryResponse>
try {
val uriBuilder = client.baseUri.buildUpon().apply {
appendEncodedPath(appUrl)
}
val getMethod = GetMethod(URL(uriBuilder.build().toString()))
val status = client.executeHttpMethod(getMethod)
val response = getMethod.getResponseBodyAsString()
if (status == HttpConstants.HTTP_OK) {
Timber.d("Successful response $response")
// Parse the response
val moshi: Moshi = Moshi.Builder().build()
val adapter: JsonAdapter<AppRegistryResponse> = moshi.adapter(AppRegistryResponse::class.java)
val appRegistryResponse: AppRegistryResponse = response?.let { adapter.fromJson(it) } ?: AppRegistryResponse(value = emptyList())
result = RemoteOperationResult(OK)
result.data = appRegistryResponse
Timber.d("Get AppRegistry completed and parsed to ${result.data}")
} else {
result = RemoteOperationResult(getMethod)
Timber.e("Failed response while getting app registry from the server status code: $status; response message: $response")
}
} catch (e: Exception) {
result = RemoteOperationResult(e)
Timber.e(e, "Exception while getting app registry")
}
return result
}
}

View File

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

View File

@ -1,56 +0,0 @@
/* ownCloud Android Library is available under MIT license
* @author Abel García de Prada
*
* Copyright (C) 2023 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.appregistry.responses
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class AppRegistryResponse(
@Json(name = "mime-types")
val value: List<AppRegistryMimeTypeResponse>
)
@JsonClass(generateAdapter = true)
data class AppRegistryMimeTypeResponse(
@Json(name = "mime_type") val mimeType: String,
val ext: String? = null,
@Json(name = "app_providers")
val appProviders: List<AppRegistryProviderResponse>,
val name: String? = null,
val icon: String? = null,
val description: String? = null,
@Json(name = "allow_creation")
val allowCreation: Boolean? = null,
@Json(name = "default_application")
val defaultApplication: String? = null
)
@JsonClass(generateAdapter = true)
data class AppRegistryProviderResponse(
val name: String,
val icon: String,
)

View File

@ -1,54 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.owncloud.android.lib.resources.appregistry.services
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.appregistry.CreateRemoteFileWithAppProviderOperation
import com.owncloud.android.lib.resources.appregistry.GetRemoteAppRegistryOperation
import com.owncloud.android.lib.resources.appregistry.GetUrlToOpenInWebRemoteOperation
import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse
class OCAppRegistryService(override val client: OwnCloudClient) : AppRegistryService {
override fun getAppRegistry(appUrl: String?): RemoteOperationResult<AppRegistryResponse> =
GetRemoteAppRegistryOperation(appUrl).execute(client)
override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String, appName: String): RemoteOperationResult<String> =
GetUrlToOpenInWebRemoteOperation(
openWithWebEndpoint = openWebEndpoint,
fileId = fileId,
appName = appName
).execute(client)
override fun createFileWithAppProvider(
createFileWithAppProviderEndpoint: String,
parentContainerId: String,
filename: String
): RemoteOperationResult<String> =
CreateRemoteFileWithAppProviderOperation(
createFileWithAppProviderEndpoint = createFileWithAppProviderEndpoint,
parentContainerId = parentContainerId,
filename = filename,
).execute(client)
}

View File

@ -1,96 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils.allPropSet
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import timber.log.Timber
import java.net.URL
import java.util.concurrent.TimeUnit
/**
* Operation to check the existence of a path in a remote server.
*
* @author David A. Velasco
* @author David González Verdugo
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
*
* @param remotePath Path to append to the URL owned by the client instance.
* @param isUserLoggedIn When `true`, the username won't be added at the end of the PROPFIND url since is not
* needed to check user credentials
*/
class CheckPathExistenceRemoteOperation(
val remotePath: String? = "",
val isUserLoggedIn: Boolean,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<Boolean>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> {
val baseStringUrl = spaceWebDavUrl ?: if (isUserLoggedIn) client.userFilesWebDavUri.toString() else client.baseFilesWebDavUri.toString()
val stringUrl = if (isUserLoggedIn) baseStringUrl + WebdavUtils.encodePath(remotePath) else baseStringUrl
return try {
val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropSet).apply {
setReadTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
}
val status = client.executeHttpMethod(propFindMethod)
/* PROPFIND method
* 404 NOT FOUND: path doesn't exist,
* 207 MULTI_STATUS: path exists.
*/
Timber.d(
"Existence check for $stringUrl finished with HTTP status $status${if (!isSuccess(status)) "(FAIL)" else ""}"
)
if (isSuccess(status)) RemoteOperationResult<Boolean>(ResultCode.OK).apply { data = true }
else RemoteOperationResult<Boolean>(propFindMethod).apply { data = false }
} catch (e: Exception) {
val result = RemoteOperationResult<Boolean>(e)
Timber.e(
e,
"Existence check for $stringUrl : ${result.logMessage}"
)
result.data = false
result
}
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
companion object {
/**
* Maximum time to wait for a response from the server in milliseconds.
*/
private const val TIMEOUT = 10000
}
}

View File

@ -1,133 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.CopyMethod
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.lib.common.utils.isOneOf
import timber.log.Timber
import java.net.URL
import java.util.concurrent.TimeUnit
/**
* Remote operation copying a remote file or folder in the ownCloud server to a different folder
* in the same account.
*
* Allows renaming the copying file/folder at the same time.
*
* @author David A. Velasco
* @author Christian Schabesberger
* @author David González V.
* @author Juan Carlos Garrote Gascón
* @author Manuel Plazas Palacio
*
* @param sourceRemotePath Remote path of the file/folder to copy.
* @param targetRemotePath Remote path desired for the file/folder to copy it.
*/
class CopyRemoteFileOperation(
private val sourceRemotePath: String,
private val targetRemotePath: String,
private val sourceSpaceWebDavUrl: String? = null,
private val targetSpaceWebDavUrl: String? = null,
private val forceOverride: Boolean = false,
) : RemoteOperation<String>() {
/**
* Performs the rename operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
if (targetRemotePath == sourceRemotePath && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) {
// nothing to do!
return RemoteOperationResult(ResultCode.OK)
}
if (targetRemotePath.startsWith(sourceRemotePath) && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) {
return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT)
}
/// perform remote operation
var result: RemoteOperationResult<String>
try {
val copyMethod = CopyMethod(
url = URL((sourceSpaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(sourceRemotePath)),
destinationUrl = (targetSpaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(targetRemotePath),
forceOverride = forceOverride,
).apply {
addRequestHeaders(this)
setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS)
setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
}
val status = client.executeHttpMethod(copyMethod)
when {
isSuccess(status) -> {
val fileRemoteId = copyMethod.getResponseHeader(HttpConstants.OC_FILE_REMOTE_ID)
result = RemoteOperationResult(ResultCode.OK)
result.setData(fileRemoteId)
}
isPreconditionFailed(status) -> {
result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE)
client.exhaustResponse(copyMethod.getResponseBodyAsStream())
/// for other errors that could be explicitly handled, check first:
/// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
}
else -> {
result = RemoteOperationResult(copyMethod)
client.exhaustResponse(copyMethod.getResponseBodyAsStream())
}
}
Timber.i("Copy $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
} catch (e: Exception) {
result = RemoteOperationResult(e)
Timber.e(e, "Copy $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
}
return result
}
private fun addRequestHeaders(copyMethod: CopyMethod) {
//Adding this because the library has an error with override
if (copyMethod.forceOverride) {
copyMethod.setRequestHeader(OVERWRITE, TRUE)
}
}
private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
private fun isPreconditionFailed(status: Int) = status == HttpConstants.HTTP_PRECONDITION_FAILED
companion object {
private const val COPY_READ_TIMEOUT = 10L
private const val COPY_CONNECTION_TIMEOUT = 6L
private const val OVERWRITE = "overwrite"
private const val TRUE = "T"
}
}

View File

@ -1,110 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.MkColMethod
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import timber.log.Timber
import java.net.URL
import java.util.concurrent.TimeUnit
/**
* Remote operation performing the creation of a new folder in the ownCloud server.
*
* @author David A. Velasco
* @author masensio
*
* @param remotePath Full path to the new directory to create in the remote server.
* @param createFullPath 'True' means that all the ancestor folders should be created.
*/
class CreateRemoteFolderOperation(
val remotePath: String,
private val createFullPath: Boolean,
private val isChunksFolder: Boolean = false,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<Unit>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
var result = createFolder(client)
if (!result.isSuccess && createFullPath && result.code == ResultCode.CONFLICT) {
result = createParentFolder(FileUtils.getParentPath(remotePath), client)
if (result.isSuccess) {
// Second and last try
result = createFolder(client)
}
}
return result
}
private fun createFolder(client: OwnCloudClient): RemoteOperationResult<Unit> {
var result: RemoteOperationResult<Unit>
try {
val webDavUri = if (isChunksFolder) {
client.uploadsWebDavUri.toString()
} else {
spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
}
val mkCol = MkColMethod(
URL(webDavUri + WebdavUtils.encodePath(remotePath))
).apply {
setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
}
val status = client.executeHttpMethod(mkCol)
result =
if (status == HttpConstants.HTTP_CREATED) {
RemoteOperationResult(ResultCode.OK)
} else {
RemoteOperationResult(mkCol)
}
Timber.d("Create directory $remotePath: ${result.logMessage}")
client.exhaustResponse(mkCol.getResponseBodyAsStream())
} catch (e: Exception) {
result = RemoteOperationResult(e)
Timber.e(e, "Create directory $remotePath: ${result.logMessage}")
}
return result
}
private fun createParentFolder(parentPath: String, client: OwnCloudClient): RemoteOperationResult<Unit> {
val operation: RemoteOperation<Unit> = CreateRemoteFolderOperation(parentPath, createFullPath)
return operation.execute(client)
}
companion object {
private const val READ_TIMEOUT: Long = 30_000
private const val CONNECTION_TIMEOUT: Long = 5_000
}
}

View File

@ -1,185 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.OperationCancelledException
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import timber.log.Timber
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.net.URL
import java.util.concurrent.atomic.AtomicBoolean
/**
* Remote operation performing the download of a remote file in the ownCloud server.
*
* @author David A. Velasco
* @author masensio
*/
class DownloadRemoteFileOperation(
private val remotePath: String,
localFolderPath: String,
private val spaceWebDavUrl: String? = null,
) : RemoteOperation<Unit>() {
private val cancellationRequested = AtomicBoolean(false)
private val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
var modificationTimestamp: Long = 0
private set
var etag: String = ""
private set
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
// download will be performed to a temporal file, then moved to the final location
val tmpFile = File(tmpPath)
// perform the download
return try {
tmpFile.parentFile?.mkdirs()
downloadFile(client, tmpFile).also { result ->
Timber.i("Download of $remotePath to $tmpPath: ${result.logMessage}")
}
} catch (e: Exception) {
RemoteOperationResult<Unit>(e).also { result ->
Timber.e(e, "Download of $remotePath to $tmpPath: ${result.logMessage}")
}
}
}
@Throws(Exception::class)
private fun downloadFile(client: OwnCloudClient, targetFile: File): RemoteOperationResult<Unit> {
val result: RemoteOperationResult<Unit>
var it: Iterator<OnDatatransferProgressListener>
var fos: FileOutputStream? = null
var bis: BufferedInputStream? = null
var savedFile = false
val webDavUri = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
val getMethod = GetMethod(URL(webDavUri + WebdavUtils.encodePath(remotePath)))
try {
val status = client.executeHttpMethod(getMethod)
if (isSuccess(status)) {
targetFile.createNewFile()
bis = BufferedInputStream(getMethod.getResponseBodyAsStream())
fos = FileOutputStream(targetFile)
var transferred: Long = 0
val contentLength = getMethod.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER)
val totalToTransfer = if (!contentLength.isNullOrEmpty()) {
contentLength.toLong()
} else {
0
}
val bytes = ByteArray(4096)
var readResult: Int
while (bis.read(bytes).also { readResult = it } != -1) {
synchronized(cancellationRequested) {
if (cancellationRequested.get()) {
getMethod.abort()
throw OperationCancelledException()
}
}
fos.write(bytes, 0, readResult)
transferred += readResult.toLong()
synchronized(dataTransferListeners) {
it = dataTransferListeners.iterator()
while (it.hasNext()) {
it.next()
.onTransferProgress(readResult.toLong(), transferred, totalToTransfer, targetFile.name)
}
}
}
if (transferred == totalToTransfer) { // Check if the file is completed
savedFile = true
val modificationTime =
getMethod.getResponseHeaders()?.get("Last-Modified")
?: getMethod.getResponseHeader("last-modified")
if (modificationTime != null) {
val modificationDate = WebdavUtils.parseResponseDate(modificationTime)
modificationTimestamp = modificationDate?.time ?: 0
} else {
Timber.e("Could not read modification time from response downloading %s", remotePath)
}
etag = WebdavUtils.getEtagFromResponse(getMethod)
// Get rid of extra quotas
etag = etag.replace("\"", "")
if (etag.isEmpty()) {
Timber.e("Could not read eTag from response downloading %s", remotePath)
}
} else {
Timber.e("Content-Length not equal to transferred bytes.")
Timber.d("totalToTransfer = $totalToTransfer, transferred = $transferred")
client.exhaustResponse(getMethod.getResponseBodyAsStream())
// TODO some kind of error control!
}
} else if (status != HttpConstants.HTTP_FORBIDDEN && status != HttpConstants.HTTP_SERVICE_UNAVAILABLE) {
client.exhaustResponse(getMethod.getResponseBodyAsStream())
} // else, body read by RemoteOperationResult constructor
result =
if (isSuccess(status)) {
RemoteOperationResult(RemoteOperationResult.ResultCode.OK)
} else {
RemoteOperationResult(getMethod)
}
} finally {
fos?.close()
bis?.close()
if (!savedFile && targetFile.exists()) {
targetFile.delete()
}
}
return result
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
private val tmpPath: String = localFolderPath + remotePath
fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
synchronized(dataTransferListeners) { dataTransferListeners.add(listener) }
}
fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener?) {
synchronized(dataTransferListeners) { dataTransferListeners.remove(listener) }
}
fun cancel() {
cancellationRequested.set(true) // atomic set; there is no need of synchronizing it
}
}

View File

@ -1,57 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import timber.log.Timber;
import java.io.File;
public class FileUtils {
public static final String FINAL_CHUNKS_FILE = ".file";
public static final String MIME_DIR = "DIR";
public static final String MIME_DIR_UNIX = "httpd/unix-directory";
public static final String MODE_READ_ONLY = "r";
static String getParentPath(String remotePath) {
String parentPath = new File(remotePath).getParent();
parentPath = parentPath.endsWith(File.separator) ? parentPath : parentPath + File.separator;
return parentPath;
}
/**
* Validate the fileName to detect if contains any forbidden character: / , \ , < , > ,
* : , " , | , ? , *
*
*/
public static boolean isValidName(String fileName) {
boolean result = true;
Timber.d("fileName =======%s", fileName);
if (fileName.contains(File.separator)) {
result = false;
}
return result;
}
}

View File

@ -1,77 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import timber.log.Timber
import java.net.URL
import java.util.concurrent.TimeUnit
/**
* Operation to get the base url, which might differ in case of a redirect.
*
* @author Christian Schabesberger
*/
class GetBaseUrlRemoteOperation : RemoteOperation<String?>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<String?> {
return try {
val stringUrl = client.baseFilesWebDavUri.toString()
val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropSet).apply {
setReadTimeout(TIMEOUT, TimeUnit.SECONDS)
setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS)
}
val status = client.executeHttpMethod(propFindMethod)
if (isSuccess(status)) {
RemoteOperationResult<String?>(RemoteOperationResult.ResultCode.OK).apply {
data = propFindMethod.getFinalUrl().toString()
}
} else {
RemoteOperationResult<String?>(propFindMethod).apply {
data = null
}
}
} catch (e: Exception) {
Timber.e(e, "Could not get actuall (or redirected) base URL from base url (/).")
RemoteOperationResult<String?>(e)
}
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
companion object {
/**
* Maximum time to wait for a response from the server in milliseconds.
*/
private const val TIMEOUT = 10_000L
}
}

View File

@ -1,149 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import android.net.Uri
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.lib.common.utils.isOneOf
import timber.log.Timber
import java.net.URL
import java.util.concurrent.TimeUnit
/**
* Remote operation moving a remote file or folder in the ownCloud server to a different folder
* in the same account and space.
*
* Allows renaming the moving file/folder at the same time.
*
* @author David A. Velasco
* @author David González Verdugo
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
* @author Manuel Plazas Palacio
*
* @param sourceRemotePath Remote path of the file/folder to copy.
* @param targetRemotePath Remote path desired for the file/folder to copy it.
*/
open class MoveRemoteFileOperation(
private val sourceRemotePath: String,
private val targetRemotePath: String,
private val spaceWebDavUrl: String? = null,
private val forceOverride: Boolean = false,
) : RemoteOperation<Unit>() {
/**
* Performs the rename operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
if (targetRemotePath == sourceRemotePath) {
// nothing to do!
return RemoteOperationResult(ResultCode.OK)
}
if (targetRemotePath.startsWith(sourceRemotePath)) {
return RemoteOperationResult(ResultCode.INVALID_MOVE_INTO_DESCENDANT)
}
/// perform remote operation
var result: RemoteOperationResult<Unit>
try {
// After finishing a chunked upload, we have to move the resulting file from uploads folder to files one,
// so this uri has to be customizable
val srcWebDavUri = getSrcWebDavUriForClient(client)
val moveMethod = MoveMethod(
url = URL((spaceWebDavUrl ?: srcWebDavUri.toString()) + WebdavUtils.encodePath(sourceRemotePath)),
destinationUrl = (spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(targetRemotePath),
forceOverride = forceOverride,
).apply {
addRequestHeaders(this)
setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS)
setConnectionTimeout(MOVE_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
}
val status = client.executeHttpMethod(moveMethod)
when {
isSuccess(status) -> {
result = RemoteOperationResult<Unit>(ResultCode.OK)
}
isPreconditionFailed(status) -> {
result = RemoteOperationResult<Unit>(ResultCode.INVALID_OVERWRITE)
client.exhaustResponse(moveMethod.getResponseBodyAsStream())
/// for other errors that could be explicitly handled, check first:
/// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
}
else -> {
result = RemoteOperationResult<Unit>(moveMethod)
client.exhaustResponse(moveMethod.getResponseBodyAsStream())
}
}
Timber.i("Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
} catch (e: Exception) {
result = RemoteOperationResult<Unit>(e)
Timber.e(e, "Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
}
return result
}
/**
* For standard moves, we will use [OwnCloudClient.getUserFilesWebDavUri].
* In case we need a different source Uri, override this method.
*/
open fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.userFilesWebDavUri
/**
* For standard moves, we won't need any special headers.
* In case new headers are needed, override this method
*/
open fun addRequestHeaders(moveMethod: MoveMethod) {
//Adding this because the library has an error with override
if (moveMethod.forceOverride) {
moveMethod.setRequestHeader(OVERWRITE, TRUE)
}
}
private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
private fun isPreconditionFailed(status: Int) = status == HttpConstants.HTTP_PRECONDITION_FAILED
companion object {
private const val MOVE_READ_TIMEOUT = 10L
private const val MOVE_CONNECTION_TIMEOUT = 6L
private const val OVERWRITE = "overwrite"
private const val TRUE = "T"
}
}

View File

@ -1,107 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.accounts.AccountUtils
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_MULTI_STATUS
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
import com.owncloud.android.lib.common.http.methods.webdav.DavConstants.DEPTH_0
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.isOneOf
import timber.log.Timber
import java.net.URL
import java.util.concurrent.TimeUnit
/**
* Remote operation performing the read a file from the ownCloud server.
*
* @author David A. Velasco
* @author masensio
* @author David González Verdugo
*/
class ReadRemoteFileOperation(
val remotePath: String,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<RemoteFile>() {
/**
* Performs the read operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteFile> {
try {
val propFind = PropfindMethod(
url = getFinalWebDavUrl(),
depth = DEPTH_0,
propertiesToRequest = DavUtils.allPropSet
).apply {
setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS)
setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
}
val status = client.executeHttpMethod(propFind)
Timber.i("Read remote file $remotePath with status ${propFind.statusCode}")
return if (isSuccess(status)) {
val remoteFile = RemoteFile.getRemoteFileFromDav(
davResource = propFind.root!!,
userId = AccountUtils.getUserId(mAccount, mContext),
userName = mAccount.name,
spaceWebDavUrl = spaceWebDavUrl,
)
RemoteOperationResult<RemoteFile>(RemoteOperationResult.ResultCode.OK).apply {
data = remoteFile
}
} else {
RemoteOperationResult<RemoteFile>(propFind).also {
client.exhaustResponse(propFind.getResponseBodyAsStream())
}
}
} catch (exception: Exception) {
return RemoteOperationResult(exception)
}
}
private fun getFinalWebDavUrl(): URL {
val baseWebDavUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
return URL(baseWebDavUrl + WebdavUtils.encodePath(remotePath))
}
private fun isSuccess(status: Int) = status.isOneOf(HTTP_MULTI_STATUS, HTTP_OK)
companion object {
private const val SYNC_READ_TIMEOUT = 40_000L
private const val SYNC_CONNECTION_TIMEOUT = 5_000L
}
}

View File

@ -1,118 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import at.bitfire.dav4jvm.PropertyRegistry
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.accounts.AccountUtils
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_MULTI_STATUS
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
import com.owncloud.android.lib.common.http.methods.webdav.DavConstants
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.lib.common.utils.isOneOf
import timber.log.Timber
import java.net.URL
/**
* Remote operation performing the read of remote file or folder in the ownCloud server.
*
* @author David A. Velasco
* @author masensio
* @author David González Verdugo
*/
class ReadRemoteFolderOperation(
val remotePath: String,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<ArrayList<RemoteFile>>() {
/**
* Performs the read operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
override fun run(client: OwnCloudClient): RemoteOperationResult<ArrayList<RemoteFile>> {
try {
PropertyRegistry.register(OCShareTypes.Factory())
val propfindMethod = PropfindMethod(
getFinalWebDavUrl(),
DavConstants.DEPTH_1,
DavUtils.allPropSet
)
val status = client.executeHttpMethod(propfindMethod)
if (isSuccess(status)) {
val mFolderAndFiles = ArrayList<RemoteFile>()
val remoteFolder = RemoteFile.getRemoteFileFromDav(
davResource = propfindMethod.root!!,
userId = AccountUtils.getUserId(mAccount, mContext),
userName = mAccount.name,
spaceWebDavUrl = spaceWebDavUrl,
)
mFolderAndFiles.add(remoteFolder)
// loop to update every child
propfindMethod.members.forEach { resource ->
val remoteFile = RemoteFile.getRemoteFileFromDav(
davResource = resource,
userId = AccountUtils.getUserId(mAccount, mContext),
userName = mAccount.name,
spaceWebDavUrl = spaceWebDavUrl,
)
mFolderAndFiles.add(remoteFile)
}
// Result of the operation
return RemoteOperationResult<ArrayList<RemoteFile>>(ResultCode.OK).apply {
data = mFolderAndFiles
Timber.i("Synchronized $remotePath with ${mFolderAndFiles.size} files. ${this.logMessage}")
}
} else { // synchronization failed
return RemoteOperationResult<ArrayList<RemoteFile>>(propfindMethod).also {
Timber.w("Synchronized $remotePath ${it.logMessage}")
}
}
} catch (e: Exception) {
return RemoteOperationResult<ArrayList<RemoteFile>>(e).also {
Timber.e(it.exception, "Synchronized $remotePath")
}
}
}
private fun getFinalWebDavUrl(): URL {
val baseWebDavUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
return URL(baseWebDavUrl + WebdavUtils.encodePath(remotePath))
}
private fun isSuccess(status: Int): Boolean = status.isOneOf(HTTP_OK, HTTP_MULTI_STATUS)
}

View File

@ -1,194 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import android.net.Uri
import android.os.Parcelable
import androidx.annotation.VisibleForTesting
import at.bitfire.dav4jvm.PropStat
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.property.CreationDate
import at.bitfire.dav4jvm.property.GetContentLength
import at.bitfire.dav4jvm.property.GetContentType
import at.bitfire.dav4jvm.property.GetETag
import at.bitfire.dav4jvm.property.GetLastModified
import at.bitfire.dav4jvm.property.OCId
import at.bitfire.dav4jvm.property.OCPermissions
import at.bitfire.dav4jvm.property.OCPrivatelink
import at.bitfire.dav4jvm.property.OCSize
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
import com.owncloud.android.lib.common.utils.isOneOf
import com.owncloud.android.lib.resources.shares.ShareType
import com.owncloud.android.lib.resources.shares.ShareType.Companion.fromValue
import kotlinx.parcelize.Parcelize
import okhttp3.HttpUrl
import timber.log.Timber
import java.io.File
/**
* Contains the data of a Remote File from a WebDavEntry
*
* The path received must be URL-decoded. Path separator must be File.separator, and it must be the first character in 'path'.
*
* @author masensio
* @author Christian Schabesberger
* @author Abel García de Prada
*/
@Parcelize
data class RemoteFile(
var remotePath: String,
var mimeType: String = "DIR",
var length: Long = 0,
var creationTimestamp: Long = 0,
var modifiedTimestamp: Long = 0,
var etag: String? = null,
var permissions: String? = null,
var remoteId: String? = null,
var size: Long = 0,
var privateLink: String? = null,
var owner: String,
var sharedByLink: Boolean = false,
var sharedWithSharee: Boolean = false,
) : Parcelable {
// TODO: Quotas not used. Use or remove them.
init {
require(
!(remotePath.isEmpty() || !remotePath.startsWith(File.separator))
) { "Trying to create a OCFile with a non valid remote path: $remotePath" }
}
/**
* Use this to find out if this file is a folder.
*
* @return true if it is a folder
*/
val isFolder
get() = mimeType.isOneOf(MIME_DIR, MIME_DIR_UNIX)
companion object {
const val MIME_DIR = "DIR"
const val MIME_DIR_UNIX = "httpd/unix-directory"
fun getRemoteFileFromDav(
davResource: Response,
userId: String,
userName: String,
spaceWebDavUrl: String? = null
): RemoteFile {
val remotePath = getRemotePathFromUrl(davResource.href, userId, spaceWebDavUrl)
val remoteFile = RemoteFile(remotePath = remotePath, owner = userName)
val properties = getPropertiesEvenIfPostProcessing(davResource)
for (property in properties) {
when (property) {
is CreationDate -> {
remoteFile.creationTimestamp = property.creationDate.toLong()
}
is GetContentLength -> {
remoteFile.length = property.contentLength
}
is GetContentType -> {
property.type?.let { remoteFile.mimeType = it }
}
is GetLastModified -> {
remoteFile.modifiedTimestamp = property.lastModified
}
is GetETag -> {
remoteFile.etag = property.eTag
}
is OCPermissions -> {
remoteFile.permissions = property.permission
}
is OCId -> {
remoteFile.remoteId = property.id
}
is OCSize -> {
remoteFile.size = property.size
}
is OCPrivatelink -> {
remoteFile.privateLink = property.link
}
is OCShareTypes -> {
val list = property.shareTypes
for (i in list.indices) {
val shareType = fromValue(list[i].toInt())
if (shareType == null) {
Timber.d("Illegal share type value: " + list[i])
continue
}
if (shareType == ShareType.PUBLIC_LINK) {
remoteFile.sharedByLink = true
} else if (shareType == ShareType.USER || shareType == ShareType.FEDERATED || shareType == ShareType.GROUP) {
remoteFile.sharedWithSharee = true
}
}
}
}
}
return remoteFile
}
/**
* Retrieves a relative path from a remote file url
*
*
* Example legacy:
* /remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt
*
* Example spaces:
* /dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$05bca744-d89f-4e9c-a990-25a0d7f03fe9/Documents/text.txt => /Documents/text.txt
*
* @param url remote file url
* @param userId file owner
* @param spaceWebDavUrl custom web dav url for space
* @return remote relative path of the file
*/
@VisibleForTesting
fun getRemotePathFromUrl(
url: HttpUrl,
userId: String,
spaceWebDavUrl: String? = null,
): String {
val davFilesPath = spaceWebDavUrl ?: (OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId)
val absoluteDavPath = if (spaceWebDavUrl != null) Uri.decode(url.toString()) else Uri.decode(url.encodedPath)
val pathToOc = absoluteDavPath.split(davFilesPath).first()
return absoluteDavPath.replace(pathToOc + davFilesPath, "")
}
private fun getPropertiesEvenIfPostProcessing(response: Response): List<Property> {
return if (response.isSuccess())
response.propstat.filter { propStat -> propStat.isSuccessOrPostProcessing() }.map { it.properties }.flatten()
else
emptyList()
}
private fun PropStat.isSuccessOrPostProcessing() = (status.code / 100 == 2 || status.code == HttpConstants.HTTP_TOO_EARLY)
}
}

View File

@ -1,81 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_NO_CONTENT
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
import com.owncloud.android.lib.common.http.methods.nonwebdav.DeleteMethod
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.lib.common.utils.isOneOf
import timber.log.Timber
import java.net.URL
/**
* Remote operation performing the removal of a remote file or folder in the ownCloud server.
*
* @author David A. Velasco
* @author masensio
* @author David González Verdugo
* @author Abel García de Prada
*/
open class RemoveRemoteFileOperation(
private val remotePath: String,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<Unit>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
var result: RemoteOperationResult<Unit>
try {
val srcWebDavUri = getSrcWebDavUriForClient(client)
val deleteMethod = DeleteMethod(
URL(srcWebDavUri + WebdavUtils.encodePath(remotePath))
)
val status = client.executeHttpMethod(deleteMethod)
result = if (isSuccess(status)) {
RemoteOperationResult<Unit>(ResultCode.OK)
} else {
RemoteOperationResult<Unit>(deleteMethod)
}
Timber.i("Remove $remotePath: ${result.logMessage}")
} catch (e: Exception) {
result = RemoteOperationResult<Unit>(e)
Timber.e(e, "Remove $remotePath: ${result.logMessage}")
}
return result
}
/**
* For standard removals, we will use [OwnCloudClient.getUserFilesWebDavUri].
* In case we need a different source Uri, override this method.
*/
open fun getSrcWebDavUriForClient(client: OwnCloudClient): String = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
private fun isSuccess(status: Int) = status.isOneOf(HTTP_OK, HTTP_NO_CONTENT)
}

View File

@ -1,120 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2021 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.lib.common.utils.isOneOf
import timber.log.Timber
import java.io.File
import java.net.URL
import java.util.concurrent.TimeUnit
/**
* Remote operation performing the rename of a remote file or folder in the ownCloud server.
*
* @author David A. Velasco
* @author masensio
*/
class RenameRemoteFileOperation(
private val oldName: String,
private val oldRemotePath: String,
private val newName: String,
isFolder: Boolean,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<Unit>() {
private var newRemotePath: String
init {
var parent = (File(oldRemotePath)).parent ?: throw IllegalArgumentException()
if (!parent.endsWith(File.separator)) {
parent = parent.plus(File.separator)
}
newRemotePath = parent.plus(newName)
if (isFolder) {
newRemotePath.plus(File.separator)
}
}
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
var result: RemoteOperationResult<Unit>
try {
if (newName == oldName) {
return RemoteOperationResult<Unit>(ResultCode.OK)
}
if (targetPathIsUsed(client)) {
return RemoteOperationResult<Unit>(ResultCode.INVALID_OVERWRITE)
}
val moveMethod: MoveMethod = MoveMethod(
url = URL((spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(oldRemotePath)),
destinationUrl = (spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(newRemotePath),
).apply {
setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.MILLISECONDS)
setConnectionTimeout(RENAME_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
}
val status = client.executeHttpMethod(moveMethod)
result = if (isSuccess(status)) {
RemoteOperationResult<Unit>(ResultCode.OK)
} else {
RemoteOperationResult<Unit>(moveMethod)
}
Timber.i("Rename $oldRemotePath to $newRemotePath: ${result.logMessage}")
client.exhaustResponse(moveMethod.getResponseBodyAsStream())
return result
} catch (exception: Exception) {
result = RemoteOperationResult<Unit>(exception)
Timber.e(exception, "Rename $oldRemotePath to $newName: ${result.logMessage}")
return result
}
}
/**
* Checks if a file with the new name already exists.
*
* @return 'True' if the target path is already used by an existing file.
*/
private fun targetPathIsUsed(client: OwnCloudClient): Boolean {
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation(newRemotePath, true)
val exists = checkPathExistenceRemoteOperation.execute(client)
return exists.isSuccess
}
private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
companion object {
private const val RENAME_READ_TIMEOUT = 10_000L
private const val RENAME_CONNECTION_TIMEOUT = 5_000L
}
}

View File

@ -1,68 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod
import com.owncloud.android.lib.common.network.ContentUriRequestBody
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.isOneOf
import timber.log.Timber
import java.net.URL
class UploadFileFromContentUriOperation(
private val uploadPath: String,
private val lastModified: String,
private val requestBody: ContentUriRequestBody
) : RemoteOperation<Unit>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
val putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(uploadPath)), requestBody).apply {
retryOnConnectionFailure = false
addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, requestBody.contentLength().toString())
addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, lastModified)
}
return try {
val status = client.executeHttpMethod(putMethod)
if (isSuccess(status)) {
RemoteOperationResult<Unit>(RemoteOperationResult.ResultCode.OK).apply { data = Unit }
} else {
RemoteOperationResult<Unit>(putMethod)
}
} catch (e: Exception) {
val result = RemoteOperationResult<Unit>(e)
Timber.e(e, "Upload from content uri failed : ${result.logMessage}")
result
}
}
fun isSuccess(status: Int): Boolean {
return status.isOneOf(HttpConstants.HTTP_OK, HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
}
}

View File

@ -1,148 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod
import com.owncloud.android.lib.common.network.FileRequestBody
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.common.operations.OperationCancelledException
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.lib.common.utils.isOneOf
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import timber.log.Timber
import java.io.File
import java.net.URL
import java.util.concurrent.atomic.AtomicBoolean
/**
* Remote operation performing the upload of a remote file to the ownCloud server.
*
* @author David A. Velasco
* @author masensio
* @author David González Verdugo
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
*/
open class UploadFileFromFileSystemOperation(
val localPath: String,
val remotePath: String,
val mimeType: String,
val lastModifiedTimestamp: String,
val requiredEtag: String?,
val spaceWebDavUrl: String? = null,
) : RemoteOperation<Unit>() {
protected val cancellationRequested = AtomicBoolean(false)
protected var putMethod: PutMethod? = null
protected val dataTransferListener: MutableSet<OnDatatransferProgressListener> = HashSet()
protected var fileRequestBody: FileRequestBody? = null
var etag: String = ""
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
var result: RemoteOperationResult<Unit>
try {
if (cancellationRequested.get()) {
// the operation was cancelled before getting it's turn to be executed in the queue of uploads
result = RemoteOperationResult<Unit>(OperationCancelledException())
Timber.i("Upload of $localPath to $remotePath has been cancelled")
} else {
// perform the upload
result = uploadFile(client)
Timber.i("Upload of $localPath to $remotePath: ${result.logMessage}")
}
} catch (e: Exception) {
if (putMethod?.isAborted == true) {
result = RemoteOperationResult<Unit>(OperationCancelledException())
Timber.e(result.exception, "Upload of $localPath to $remotePath has been aborted with this message: ${result.logMessage}")
} else {
result = RemoteOperationResult<Unit>(e)
Timber.e(result.exception, "Upload of $localPath to $remotePath has failed with this message: ${result.logMessage}")
}
}
return result
}
@Throws(Exception::class)
protected open fun uploadFile(client: OwnCloudClient): RemoteOperationResult<Unit> {
val fileToUpload = File(localPath)
val mediaType: MediaType? = mimeType.toMediaTypeOrNull()
fileRequestBody = FileRequestBody(fileToUpload, mediaType).also {
synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) }
}
val baseStringUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
putMethod = PutMethod(URL(baseStringUrl + WebdavUtils.encodePath(remotePath)), fileRequestBody!!).apply {
retryOnConnectionFailure = false
if (!requiredEtag.isNullOrBlank()) {
addRequestHeader(HttpConstants.IF_MATCH_HEADER, requiredEtag)
}
addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, fileToUpload.length().toString())
addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, lastModifiedTimestamp)
}
val status = client.executeHttpMethod(putMethod)
return if (isSuccess(status)) {
etag = WebdavUtils.getEtagFromResponse(putMethod)
// Get rid of extra quotas
etag = etag.replace("\"", "")
if (etag.isEmpty()) {
Timber.e("Could not read eTag from response uploading %s", localPath)
} else {
Timber.d("File uploaded successfully. New etag for file ${fileToUpload.name} is $etag")
}
RemoteOperationResult<Unit>(ResultCode.OK).apply { data = Unit }
} else { // synchronization failed
RemoteOperationResult<Unit>(putMethod)
}
}
fun addDataTransferProgressListener(listener: OnDatatransferProgressListener) {
synchronized(dataTransferListener) { dataTransferListener.add(listener) }
fileRequestBody?.addDatatransferProgressListener(listener)
}
fun removeDataTransferProgressListener(listener: OnDatatransferProgressListener) {
synchronized(dataTransferListener) { dataTransferListener.remove(listener) }
fileRequestBody?.removeDatatransferProgressListener(listener)
}
fun cancel() {
synchronized(cancellationRequested) {
cancellationRequested.set(true)
putMethod?.abort()
}
}
fun isSuccess(status: Int): Boolean {
return status.isOneOf(HttpConstants.HTTP_OK, HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
}
}

View File

@ -1,122 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2022 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files.chunks
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod
import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody
import com.owncloud.android.lib.common.operations.OperationCancelledException
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.lib.resources.files.FileUtils.MODE_READ_ONLY
import com.owncloud.android.lib.resources.files.UploadFileFromFileSystemOperation
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import timber.log.Timber
import java.io.File
import java.io.RandomAccessFile
import java.net.URL
import java.nio.channels.FileChannel
import java.util.concurrent.TimeUnit
import kotlin.math.ceil
/**
* Remote operation performing the chunked upload of a remote file to the ownCloud server.
*
* @author David A. Velasco
* @author David González Verdugo
* @author Abel García de Prada
*/
class ChunkedUploadFromFileSystemOperation(
private val transferId: String,
localPath: String,
remotePath: String,
mimeType: String,
lastModifiedTimestamp: String,
requiredEtag: String?,
) : UploadFileFromFileSystemOperation(
localPath = localPath,
remotePath = remotePath,
mimeType = mimeType,
lastModifiedTimestamp = lastModifiedTimestamp,
requiredEtag = requiredEtag
) {
@Throws(Exception::class)
override fun uploadFile(client: OwnCloudClient): RemoteOperationResult<Unit> {
lateinit var result: RemoteOperationResult<Unit>
val fileToUpload = File(localPath)
val mediaType: MediaType? = mimeType.toMediaTypeOrNull()
val raf = RandomAccessFile(fileToUpload, MODE_READ_ONLY)
val channel: FileChannel = raf.channel
val fileRequestBody = ChunkFromFileRequestBody(fileToUpload, mediaType, channel).also {
synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) }
}
val uriPrefix = client.uploadsWebDavUri.toString() + File.separator + transferId
val totalLength = fileToUpload.length()
val chunkCount = ceil(totalLength.toDouble() / CHUNK_SIZE).toLong()
var offset: Long = 0
for (chunkIndex in 0..chunkCount) {
fileRequestBody.setOffset(offset)
if (cancellationRequested.get()) {
result = RemoteOperationResult<Unit>(OperationCancelledException())
break
} else {
putMethod = PutMethod(URL(uriPrefix + File.separator + chunkIndex), fileRequestBody).apply {
if (chunkIndex == chunkCount - 1) {
// Added a high timeout to the last chunk due to when the last chunk
// arrives to the server with the last PUT, all chunks get assembled
// within that PHP request, so last one takes longer.
setReadTimeout(LAST_CHUNK_TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
}
}
val status = client.executeHttpMethod(putMethod)
Timber.d("Upload of $localPath to $remotePath, chunk index $chunkIndex, count $chunkCount, HTTP result status $status")
if (isSuccess(status)) {
result = RemoteOperationResult<Unit>(ResultCode.OK)
} else {
result = RemoteOperationResult<Unit>(putMethod)
break
}
}
offset += CHUNK_SIZE
}
channel.close()
raf.close()
return result
}
companion object {
const val CHUNK_SIZE = 10_240_000L // 10 MB
private const val LAST_CHUNK_TIMEOUT = 900_000 // 15 mins.
}
}

View File

@ -1,58 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2021 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files.chunks
import android.net.Uri
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod
import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation
/**
* Remote operation to move the file built from chunks after uploading it
*
* @author David González Verdugo
* @author Abel García de Prada
*/
class MoveRemoteChunksFileOperation(
sourceRemotePath: String,
targetRemotePath: String,
private val fileLastModificationTimestamp: String,
private val fileLength: Long
) : MoveRemoteFileOperation(
sourceRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath,
) {
override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri
override fun addRequestHeaders(moveMethod: MoveMethod) {
super.addRequestHeaders(moveMethod)
moveMethod.apply {
addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, fileLastModificationTimestamp)
addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, fileLength.toString())
}
}
}

View File

@ -1,32 +0,0 @@
/* ownCloud Android Library is available under MIT license
* @author David González Verdugo
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files.chunks
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation
class RemoveRemoteChunksFolderOperation(remotePath: String) : RemoveRemoteFileOperation(remotePath) {
override fun getSrcWebDavUriForClient(client: OwnCloudClient): String = client.uploadsWebDavUri.toString()
}

View File

@ -1,86 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files.services
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.Service
import com.owncloud.android.lib.resources.files.RemoteFile
interface FileService : Service {
fun checkPathExistence(
path: String,
isUserLogged: Boolean,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<Boolean>
fun copyFile(
sourceRemotePath: String,
targetRemotePath: String,
sourceSpaceWebDavUrl: String?,
targetSpaceWebDavUrl: String?,
replace: Boolean,
): RemoteOperationResult<String?>
fun createFolder(
remotePath: String,
createFullPath: Boolean,
isChunkFolder: Boolean = false,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<Unit>
fun downloadFile(
remotePath: String,
localTempPath: String
): RemoteOperationResult<Unit>
fun moveFile(
sourceRemotePath: String,
targetRemotePath: String,
spaceWebDavUrl: String?,
replace: Boolean,
): RemoteOperationResult<Unit>
fun readFile(
remotePath: String,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<RemoteFile>
fun refreshFolder(
remotePath: String,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<ArrayList<RemoteFile>>
fun removeFile(
remotePath: String,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<Unit>
fun renameFile(
oldName: String,
oldRemotePath: String,
newName: String,
isFolder: Boolean,
spaceWebDavUrl: String? = null,
): RemoteOperationResult<Unit>
}

View File

@ -1,50 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2021 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files.services.implementation
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.files.chunks.MoveRemoteChunksFileOperation
import com.owncloud.android.lib.resources.files.chunks.RemoveRemoteChunksFolderOperation
import com.owncloud.android.lib.resources.files.services.ChunkService
class OCChunkService(override val client: OwnCloudClient) : ChunkService {
override fun removeFile(remotePath: String): RemoteOperationResult<Unit> =
RemoveRemoteChunksFolderOperation(remotePath = remotePath).execute(client)
override fun moveFile(
sourceRemotePath: String,
targetRemotePath: String,
fileLastModificationTimestamp: String,
fileLength: Long
): RemoteOperationResult<Unit> =
MoveRemoteChunksFileOperation(
sourceRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath,
fileLastModificationTimestamp = fileLastModificationTimestamp,
fileLength = fileLength,
).execute(client)
}

View File

@ -1,143 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2023 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files.services.implementation
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
import com.owncloud.android.lib.resources.files.CopyRemoteFileOperation
import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation
import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation
import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation
import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation
import com.owncloud.android.lib.resources.files.RemoteFile
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation
import com.owncloud.android.lib.resources.files.RenameRemoteFileOperation
import com.owncloud.android.lib.resources.files.services.FileService
class OCFileService(override val client: OwnCloudClient) : FileService {
override fun checkPathExistence(
path: String,
isUserLogged: Boolean,
spaceWebDavUrl: String?,
): RemoteOperationResult<Boolean> =
CheckPathExistenceRemoteOperation(
remotePath = path,
isUserLoggedIn = isUserLogged,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
override fun copyFile(
sourceRemotePath: String,
targetRemotePath: String,
sourceSpaceWebDavUrl: String?,
targetSpaceWebDavUrl: String?,
replace: Boolean,
): RemoteOperationResult<String?> =
CopyRemoteFileOperation(
sourceRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath,
sourceSpaceWebDavUrl = sourceSpaceWebDavUrl,
targetSpaceWebDavUrl = targetSpaceWebDavUrl,
forceOverride = replace,
).execute(client)
override fun createFolder(
remotePath: String,
createFullPath: Boolean,
isChunkFolder: Boolean,
spaceWebDavUrl: String?,
): RemoteOperationResult<Unit> =
CreateRemoteFolderOperation(
remotePath = remotePath,
createFullPath = createFullPath,
isChunksFolder = isChunkFolder,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
override fun downloadFile(
remotePath: String,
localTempPath: String
): RemoteOperationResult<Unit> =
DownloadRemoteFileOperation(
remotePath = remotePath,
localFolderPath = localTempPath
).execute(client)
override fun moveFile(
sourceRemotePath: String,
targetRemotePath: String,
spaceWebDavUrl: String?,
replace: Boolean,
): RemoteOperationResult<Unit> =
MoveRemoteFileOperation(
sourceRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath,
spaceWebDavUrl = spaceWebDavUrl,
forceOverride = replace,
).execute(client)
override fun readFile(
remotePath: String,
spaceWebDavUrl: String?,
): RemoteOperationResult<RemoteFile> =
ReadRemoteFileOperation(
remotePath = remotePath,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
override fun refreshFolder(
remotePath: String,
spaceWebDavUrl: String?,
): RemoteOperationResult<ArrayList<RemoteFile>> =
ReadRemoteFolderOperation(
remotePath = remotePath,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
override fun removeFile(
remotePath: String,
spaceWebDavUrl: String?,
): RemoteOperationResult<Unit> =
RemoveRemoteFileOperation(
remotePath = remotePath,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
override fun renameFile(
oldName: String,
oldRemotePath: String,
newName: String,
isFolder: Boolean,
spaceWebDavUrl: String?,
): RemoteOperationResult<Unit> =
RenameRemoteFileOperation(
oldName = oldName,
oldRemotePath = oldRemotePath,
newName = newName,
isFolder = isFolder,
spaceWebDavUrl = spaceWebDavUrl,
).execute(client)
}

View File

@ -1,94 +0,0 @@
/* 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)
}
getMethod.followRedirects = true
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"
}
}

View File

@ -1,85 +0,0 @@
/* 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)
}
}
}

View File

@ -1,87 +0,0 @@
/* 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)
}
}
}

View File

@ -1,57 +0,0 @@
/* 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"
}
}

View File

@ -1,71 +0,0 @@
/* ownCloud Android Library is available under MIT license
*
* Copyright (C) 2020 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.owncloud.android.lib.resources.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,
val codeVerifier: 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)
.add(HttpConstants.OAUTH_HEADER_CODE_VERIFIER, codeVerifier)
.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()
}
}

View File

@ -1,42 +0,0 @@
/* 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,
)

View File

@ -1,43 +0,0 @@
/* 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?,
)

Some files were not shown because too many files have changed in this diff Show More