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

Merge pull request #338 from owncloud/master

1.0.6 stable
This commit is contained in:
Abel García de Prada 2020-08-12 16:35:18 +02:00 committed by GitHub
commit 3ea269fd98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 1091 additions and 1512 deletions

View File

@ -44,11 +44,11 @@ ownCloud Android Library is available under MIT license. See the file LICENSE.md
#### Third party libraries
ownCloud Android Library uses OkHttp version 3.10, licensed under Apache License and version 2.0. Besides, it uses Dav4Android, licensed under Mozilla Public License, v. 2.0
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
### Compatibility
ownCloud Android Library is valid for Android systems from version Android 2.2 (android:minSdkVersion="8" android:targetSdkVersion="19").
ownCloud Android Library is valid for Android systems from version Android 5 (android:minSdkVersion="21" android:targetSdkVersion="29").
ownCloud Android library supports ownCloud server from version 4.5.

View File

@ -9,7 +9,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath 'com.android.tools.build:gradle:4.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion"
}

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip

View File

@ -4,9 +4,9 @@ apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-allopen'
dependencies {
api 'com.squareup.okhttp3:okhttp:3.12.0'
api 'com.squareup.okhttp3:okhttp:4.6.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
api 'com.gitlab.ownclouders:dav4android:oc_support_1.0.1'
api 'com.gitlab.ownclouders:dav4android:oc_support_2.1.5'
api 'com.github.hannesa2:Logcat:1.6.0'
api 'net.openid:appauth:0.7.1'
@ -23,14 +23,14 @@ allOpen {
}
android {
compileSdkVersion 28
compileSdkVersion 29
defaultConfig {
minSdkVersion 21
targetSdkVersion 28
targetSdkVersion 29
versionCode = 10000500
versionName = "1.0.5"
versionCode = 10000600
versionName = "1.0.6"
// This is pretty ugly but manifest placeholders don't seem to work very well when using different modules
// See https://github.com/openid/AppAuth-Android/issues/325

View File

@ -29,7 +29,7 @@ import android.accounts.AccountManager;
import android.accounts.AccountsException;
import android.net.Uri;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4jvm.exception.HttpException;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
@ -89,11 +89,6 @@ public class OwnCloudClient extends HttpClient {
if (!(mCredentials instanceof OwnCloudAnonymousCredentials)) {
mCredentials = OwnCloudCredentialsFactory.getAnonymousCredentials();
}
mCredentials.applyTo(this);
}
void applyCredentials() {
mCredentials.applyTo(this);
}
public int executeHttpMethod(HttpBaseMethod method) throws Exception {
@ -102,8 +97,17 @@ public class OwnCloudClient extends HttpClient {
int status;
do {
setRequestId(method);
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.PARAM_SINGLE_COOKIE_HEADER, "true");
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
if (mCredentials.getHeaderAuth() != null) {
method.setRequestHeader(HttpConstants.AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
}
status = method.execute();
if (mFollowRedirects) {
@ -125,8 +129,17 @@ public class OwnCloudClient extends HttpClient {
int status;
do {
setRequestId(method);
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(OC_X_REQUEST_ID, requestId);
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
method.setRequestHeader(HttpConstants.PARAM_SINGLE_COOKIE_HEADER, "true");
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
if (mCredentials.getHeaderAuth() != null) {
method.setRequestHeader(HttpConstants.AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
}
status = method.execute();
repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter);
@ -138,19 +151,6 @@ public class OwnCloudClient extends HttpClient {
return status;
}
private void setRequestId(HttpBaseMethod method) {
// Clean previous request id. This is a bit hacky but is the only way to add request headers in WebDAV
// methods by using Dav4Android
deleteHeaderForAllRequests(OC_X_REQUEST_ID);
String requestId = RandomUtils.generateRandomUUID();
// Header to allow tracing requests in apache and ownCloud logs
addHeaderForAllRequests(OC_X_REQUEST_ID, requestId);
Timber.d("Executing in request with id %s", requestId);
}
public RedirectionPath followRedirection(HttpBaseMethod method) throws Exception {
int redirectionsCount = 0;
int status = method.getStatusCode();
@ -215,9 +215,6 @@ public class OwnCloudClient extends HttpClient {
public void exhaustResponse(InputStream responseBodyAsStream) {
if (responseBodyAsStream != null) {
try {
while (responseBodyAsStream.read(sExhaustBuffer) >= 0) {
;
}
responseBodyAsStream.close();
} catch (IOException io) {
@ -273,7 +270,6 @@ public class OwnCloudClient extends HttpClient {
public void setCredentials(OwnCloudCredentials credentials) {
if (credentials != null) {
mCredentials = credentials;
mCredentials.applyTo(this);
} else {
clearCredentials();
}

View File

@ -32,6 +32,7 @@ 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.http.HttpClient;
import timber.log.Timber;
@ -129,7 +130,6 @@ public class SingleSessionManager {
Timber.v("reusing client for session %s", sessionName);
}
keepCredentialsUpdated(client);
keepCookiesUpdated(context, account, client);
keepUriUpdated(account, client);
}
@ -176,10 +176,6 @@ public class SingleSessionManager {
Timber.d("All sessions saved");
}
private void keepCredentialsUpdated(OwnCloudClient reusedClient) {
reusedClient.applyCredentials();
}
private void keepCookiesUpdated(Context context, OwnCloudAccount account, OwnCloudClient reusedClient) {
AccountManager am = AccountManager.get(context.getApplicationContext());
if (am != null && account.getSavedAccount() != null) {
@ -191,6 +187,15 @@ public class SingleSessionManager {
}
}
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) {

View File

@ -41,6 +41,7 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import okhttp3.Cookie;
import timber.log.Timber;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -249,7 +250,7 @@ public class AccountUtils {
.domain(serverUri.getHost())
.path(
serverUri.getPath().equals("")
? FileUtils.PATH_SEPARATOR
? File.separator
: serverUri.getPath()
)
.build());

View File

@ -23,11 +23,9 @@
*/
package com.owncloud.android.lib.common.authentication;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import okhttp3.Credentials;
import okhttp3.internal.Util;
import static java.nio.charset.StandardCharsets.UTF_8;
public class OwnCloudBasicCredentials implements OwnCloudCredentials {
@ -39,21 +37,6 @@ public class OwnCloudBasicCredentials implements OwnCloudCredentials {
mPassword = password != null ? password : "";
}
public OwnCloudBasicCredentials(String username, String password, boolean preemptiveMode) {
mUsername = username != null ? username : "";
mPassword = password != null ? password : "";
}
@Override
public void applyTo(OwnCloudClient client) {
// Clear previous basic credentials
HttpClient.deleteHeaderForAllRequests(HttpConstants.AUTHORIZATION_HEADER);
HttpClient.deleteHeaderForAllRequests(HttpConstants.COOKIE_HEADER);
HttpClient.addHeaderForAllRequests(HttpConstants.AUTHORIZATION_HEADER,
Credentials.basic(mUsername, mPassword, Util.UTF_8));
}
@Override
public String getUsername() {
return mUsername;
@ -64,6 +47,11 @@ public class OwnCloudBasicCredentials implements OwnCloudCredentials {
return mPassword;
}
@Override
public String getHeaderAuth() {
return Credentials.basic(mUsername, mPassword, UTF_8);
}
@Override
public boolean authTokenExpires() {
return false;

View File

@ -23,8 +23,6 @@
*/
package com.owncloud.android.lib.common.authentication;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpClient;
import com.owncloud.android.lib.common.http.HttpConstants;
public class OwnCloudBearerCredentials implements OwnCloudCredentials {
@ -37,16 +35,6 @@ public class OwnCloudBearerCredentials implements OwnCloudCredentials {
mAccessToken = accessToken != null ? accessToken : "";
}
@Override
public void applyTo(OwnCloudClient client) {
// Clear previous credentials
HttpClient.deleteHeaderForAllRequests(HttpConstants.AUTHORIZATION_HEADER);
HttpClient.deleteHeaderForAllRequests(HttpConstants.COOKIE_HEADER);
HttpClient.addHeaderForAllRequests(HttpConstants.AUTHORIZATION_HEADER,
HttpConstants.BEARER_AUTHORIZATION_KEY + mAccessToken);
}
@Override
public String getUsername() {
// not relevant for authentication, but relevant for informational purposes
@ -58,6 +46,11 @@ public class OwnCloudBearerCredentials implements OwnCloudCredentials {
return mAccessToken;
}
@Override
public String getHeaderAuth() {
return HttpConstants.BEARER_AUTHORIZATION_KEY + mAccessToken;
}
@Override
public boolean authTokenExpires() {
return true;

View File

@ -24,16 +24,14 @@
package com.owncloud.android.lib.common.authentication;
import com.owncloud.android.lib.common.OwnCloudClient;
public interface OwnCloudCredentials {
void applyTo(OwnCloudClient ownCloudClient);
String getUsername();
String getAuthToken();
String getHeaderAuth();
boolean authTokenExpires();
boolean authTokenCanBeRefreshed();

View File

@ -55,14 +55,12 @@ public class OwnCloudCredentialsFactory {
}
@Override
public void applyTo(OwnCloudClient client) {
// Clear previous basic credentials
HttpClient.deleteHeaderForAllRequests(HttpConstants.AUTHORIZATION_HEADER);
HttpClient.deleteHeaderForAllRequests(HttpConstants.COOKIE_HEADER);
public String getAuthToken() {
return "";
}
@Override
public String getAuthToken() {
public String getHeaderAuth() {
return "";
}

View File

@ -26,9 +26,6 @@ package com.owncloud.android.lib.common.http;
import android.content.Context;
import com.owncloud.android.lib.common.SingleSessionManager;
import com.owncloud.android.lib.common.http.interceptors.HttpInterceptor;
import com.owncloud.android.lib.common.http.interceptors.RequestHeaderInterceptor;
import com.owncloud.android.lib.common.network.AdvancedX509TrustManager;
import com.owncloud.android.lib.common.network.NetworkUtils;
import okhttp3.Cookie;
@ -58,7 +55,6 @@ import java.util.concurrent.TimeUnit;
*/
public class HttpClient {
private static OkHttpClient sOkHttpClient;
private static HttpInterceptor sOkHttpInterceptor;
private static Context sContext;
private static HashMap<String, List<Cookie>> sCookieStore = new HashMap<>();
@ -71,16 +67,21 @@ public class HttpClient {
SSLContext sslContext;
try {
sslContext = SSLContext.getInstance("TLSv1.2");
} catch (NoSuchAlgorithmException tlsv12Exception) {
sslContext = SSLContext.getInstance("TLSv1.3");
} catch (NoSuchAlgorithmException tlsv13Exception) {
try {
Timber.w("TLSv1.2 is not supported in this device; falling through TLSv1.1");
sslContext = SSLContext.getInstance("TLSv1.1");
} catch (NoSuchAlgorithmException tlsv11Exception) {
Timber.w("TLSv1.1 is not supported in this device; falling through TLSv1.0");
sslContext = SSLContext.getInstance("TLSv1");
// should be available in any device; see reference of supported protocols in
// http://developer.android.com/reference/javax/net/ssl/SSLSocket.html
Timber.w("TLSv1.3 is not supported in this device; falling through TLSv1.2");
sslContext = SSLContext.getInstance("TLSv1.2");
} catch (NoSuchAlgorithmException tlsv12Exception) {
try {
Timber.w("TLSv1.2 is not supported in this device; falling through TLSv1.1");
sslContext = SSLContext.getInstance("TLSv1.1");
} catch (NoSuchAlgorithmException tlsv11Exception) {
Timber.w("TLSv1.1 is not supported in this device; falling through TLSv1.0");
sslContext = SSLContext.getInstance("TLSv1");
// should be available in any device; see reference of supported protocols in
// http://developer.android.com/reference/javax/net/ssl/SSLSocket.html
}
}
}
@ -109,7 +110,6 @@ public class HttpClient {
};
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
.addInterceptor(getOkHttpInterceptor())
.protocols(Arrays.asList(Protocol.HTTP_1_1))
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
@ -129,36 +129,6 @@ public class HttpClient {
return sOkHttpClient;
}
private static HttpInterceptor getOkHttpInterceptor() {
if (sOkHttpInterceptor == null) {
sOkHttpInterceptor = new HttpInterceptor();
addHeaderForAllRequests(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
addHeaderForAllRequests(HttpConstants.PARAM_SINGLE_COOKIE_HEADER, "true");
addHeaderForAllRequests(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
}
return sOkHttpInterceptor;
}
/**
* Add header that will be included for all the requests from now on
*
* @param headerName
* @param headerValue
*/
public static void addHeaderForAllRequests(String headerName, String headerValue) {
HttpInterceptor httpInterceptor = getOkHttpInterceptor();
if (getOkHttpInterceptor() != null) {
httpInterceptor.addRequestInterceptor(
new RequestHeaderInterceptor(headerName, headerValue)
);
}
}
public static void deleteHeaderForAllRequests(String headerName) {
getOkHttpInterceptor().deleteRequestHeaderInterceptor(headerName);
}
public Context getContext() {
return sContext;
}

View File

@ -74,8 +74,8 @@ public class TLSSocketFactory extends SSLSocketFactory {
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
if((socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2", "TLSv1.3"});
}
return socket;
}

View File

@ -1,116 +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.interceptors;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
import java.util.ArrayList;
import java.util.ListIterator;
/**
* Http interceptor to use multiple interceptors in the same {@link okhttp3.OkHttpClient} instance
*
* @author David González Verdugo
*/
public class HttpInterceptor implements Interceptor {
private final ArrayList<RequestInterceptor> mRequestInterceptors = new ArrayList<>();
private final ArrayList<ResponseInterceptor> mResponseInterceptors = new ArrayList<>();
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
ListIterator<RequestInterceptor> requestInterceptorIterator = mRequestInterceptors.listIterator();
while (requestInterceptorIterator.hasNext()) {
RequestInterceptor currentRequestInterceptor = requestInterceptorIterator.next();
request = currentRequestInterceptor.intercept(request);
}
Response response = chain.proceed(request);
ListIterator<ResponseInterceptor> responseInterceptorIterator = mResponseInterceptors.listIterator();
while (responseInterceptorIterator.hasNext()) {
ResponseInterceptor currentResponseInterceptor = responseInterceptorIterator.next();
response = currentResponseInterceptor.intercept(response);
}
return response;
}
public HttpInterceptor addRequestInterceptor(RequestInterceptor requestInterceptor) {
mRequestInterceptors.listIterator().add(requestInterceptor);
return this;
}
public HttpInterceptor addResponseInterceptor(ResponseInterceptor responseInterceptor) {
mResponseInterceptors.listIterator().add(responseInterceptor);
return this;
}
public ArrayList<RequestInterceptor> getRequestInterceptors() {
return mRequestInterceptors;
}
private ArrayList<RequestHeaderInterceptor> getRequestHeaderInterceptors() {
ArrayList<RequestHeaderInterceptor> requestHeaderInterceptors = new ArrayList<>();
for (RequestInterceptor requestInterceptor : mRequestInterceptors) {
if (requestInterceptor instanceof RequestHeaderInterceptor) {
requestHeaderInterceptors.add((RequestHeaderInterceptor) requestInterceptor);
}
}
return requestHeaderInterceptors;
}
public void deleteRequestHeaderInterceptor(String headerName) {
ListIterator<RequestInterceptor> requestInterceptorIterator = mRequestInterceptors.listIterator();
while (requestInterceptorIterator.hasNext()) {
RequestInterceptor currentRequestInterceptor = requestInterceptorIterator.next();
if (currentRequestInterceptor instanceof RequestHeaderInterceptor &&
((RequestHeaderInterceptor) currentRequestInterceptor).getHeaderName().equals(headerName)) {
requestInterceptorIterator.remove();
}
}
}
public ArrayList<ResponseInterceptor> getResponseInterceptors() {
return mResponseInterceptors;
}
public interface RequestInterceptor {
Request intercept(Request request) throws IOException;
}
public interface ResponseInterceptor {
Response intercept(Response response) throws IOException;
}
}

View File

@ -1,54 +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.interceptors;
import okhttp3.Request;
/**
* Intercept requests to update their headers
*/
public class RequestHeaderInterceptor implements HttpInterceptor.RequestInterceptor {
private String mHeaderName;
private String mHeaderValue;
public RequestHeaderInterceptor(String headerName, String headerValue) {
this.mHeaderName = headerName;
this.mHeaderValue = headerValue;
}
@Override
public Request intercept(Request request) {
return request.newBuilder().addHeader(mHeaderName, mHeaderValue).build();
}
public String getHeaderName() {
return mHeaderName;
}
public String getHeaderValue() {
return mHeaderValue;
}
}

View File

@ -1,191 +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;
import com.owncloud.android.lib.common.http.HttpClient;
import okhttp3.Call;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.concurrent.TimeUnit;
/**
* Wrapper to perform http calls transparently by using:
* - OkHttp for non webdav methods
* - Dav4Android for webdav methods
*
* @author David González Verdugo
*/
public abstract class HttpBaseMethod {
protected OkHttpClient mOkHttpClient;
protected Request mRequest;
protected RequestBody mRequestBody;
protected Response mResponse;
protected String mResponseBodyString;
protected Call mCall;
protected HttpBaseMethod(URL url) {
mOkHttpClient = HttpClient.getOkHttpClient();
mRequest = new Request.Builder()
.url(HttpUrl.parse(url.toString()))
.build();
}
public int execute() throws Exception {
return onExecute();
}
public void abort() {
mCall.cancel();
}
public boolean isAborted() {
return mCall.isCanceled();
}
//////////////////////////////
// For override
//////////////////////////////
protected abstract int onExecute() throws Exception;
//////////////////////////////
// Getter
//////////////////////////////
// Request
public Headers getRequestHeaders() {
return mRequest.headers();
}
public String getRequestHeader(String name) {
return mRequest.header(name);
}
// Response
public int getStatusCode() {
return mResponse.code();
}
public String getStatusMessage() {
return mResponse.message();
}
public String getResponseBodyAsString() throws IOException {
if (mResponseBodyString == null && mResponse.body() != null) {
mResponseBodyString = mResponse.body().string();
}
return mResponseBodyString;
}
public InputStream getResponseBodyAsStream() {
if (mResponse.body() != null) {
return mResponse.body().byteStream();
}
return null;
}
public Headers getResponseHeaders() {
return mResponse.headers();
}
public String getResponseHeader(String headerName) {
return mResponse.header(headerName);
}
public boolean getRetryOnConnectionFailure() {
return mOkHttpClient.retryOnConnectionFailure();
}
//////////////////////////////
// Setter
//////////////////////////////
// Connection parameters
public void setRetryOnConnectionFailure(boolean retryOnConnectionFailure) {
mOkHttpClient = mOkHttpClient.newBuilder()
.retryOnConnectionFailure(retryOnConnectionFailure)
.build();
}
public void setReadTimeout(long readTimeout, TimeUnit timeUnit) {
mOkHttpClient = mOkHttpClient.newBuilder()
.readTimeout(readTimeout, timeUnit)
.build();
}
public void setConnectionTimeout(long connectionTimeout, TimeUnit timeUnit) {
mOkHttpClient = mOkHttpClient.newBuilder()
.readTimeout(connectionTimeout, timeUnit)
.build();
}
public void setFollowRedirects(boolean followRedirects) {
mOkHttpClient = mOkHttpClient.newBuilder()
.followRedirects(followRedirects)
.build();
}
// Request
public void addRequestHeader(String name, String value) {
mRequest = mRequest.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
*/
public void setRequestHeader(String name, String value) {
mRequest = mRequest.newBuilder()
.header(name, value)
.build();
}
public void setRequestBody(RequestBody requestBody) {
mRequestBody = requestBody;
}
public void setUrl(HttpUrl url) {
mRequest = mRequest.newBuilder()
.url(url)
.build();
}
}

View File

@ -0,0 +1,152 @@
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 okHttpClient: OkHttpClient
var httpUrl: HttpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException()
var request: Request
abstract var response: Response
var call: Call? = null
init {
okHttpClient = HttpClient.getOkHttpClient()
request = Request.Builder()
.url(httpUrl)
.build()
}
@Throws(Exception::class)
open fun execute(): Int {
return onExecute()
}
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()
}
/*************************
*** Connection Params ***
*************************/
//////////////////////////////
// Setter
//////////////////////////////
// Connection parameters
open fun setRetryOnConnectionFailure(retryOnConnectionFailure: Boolean) {
okHttpClient = okHttpClient.newBuilder()
.retryOnConnectionFailure(retryOnConnectionFailure)
.build()
}
open fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) {
okHttpClient = okHttpClient.newBuilder()
.readTimeout(readTimeout, timeUnit)
.build()
}
open fun setConnectionTimeout(
connectionTimeout: Long,
timeUnit: TimeUnit
) {
okHttpClient = okHttpClient.newBuilder()
.readTimeout(connectionTimeout, timeUnit)
.build()
}
open fun setFollowRedirects(followRedirects: Boolean) {
okHttpClient = okHttpClient.newBuilder()
.followRedirects(followRedirects)
.build()
}
/************
*** Call ***
************/
open fun abort() {
call?.cancel()
}
open val isAborted: Boolean
get() = call?.isCanceled() ?: false
//////////////////////////////
// For override
//////////////////////////////
@Throws(Exception::class)
protected abstract fun onExecute(): Int
}

View File

@ -21,29 +21,22 @@
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.nonwebdav
package com.owncloud.android.lib.common.http.methods.nonwebdav;
import java.io.IOException;
import java.net.URL;
import java.io.IOException
import java.net.URL
/**
* OkHttp delete calls wrapper
*
* @author David González Verdugo
*/
public class DeleteMethod extends HttpMethod {
public DeleteMethod(URL url) {
super(url);
}
@Override
public int onExecute() throws IOException {
mRequest = mRequest.newBuilder()
.delete()
.build();
return super.onExecute();
class DeleteMethod(url: URL) : HttpMethod(url) {
@Throws(IOException::class)
override fun onExecute(): Int {
request = request.newBuilder()
.delete()
.build()
return super.onExecute()
}
}

View File

@ -21,29 +21,22 @@
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.nonwebdav
package com.owncloud.android.lib.common.http.methods.nonwebdav;
import java.io.IOException;
import java.net.URL;
import java.io.IOException
import java.net.URL
/**
* OkHttp get calls wrapper
*
* @author David González Verdugo
*/
public class GetMethod extends HttpMethod {
public GetMethod(URL url) {
super(url);
}
@Override
public int onExecute() throws IOException {
mRequest = mRequest.newBuilder()
.get()
.build();
return super.onExecute();
class GetMethod(url: URL) : HttpMethod(url) {
@Throws(IOException::class)
override fun onExecute(): Int {
request = request.newBuilder()
.get()
.build()
return super.onExecute()
}
}

View File

@ -21,29 +21,26 @@
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.nonwebdav
package com.owncloud.android.lib.common.http.methods.nonwebdav;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import java.io.IOException;
import java.net.URL;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
import okhttp3.Response
import java.net.URL
/**
* Wrapper to perform OkHttp calls
*
* @author David González Verdugo
*/
public abstract class HttpMethod extends HttpBaseMethod {
abstract class HttpMethod(
url: URL
) : HttpBaseMethod(url) {
public HttpMethod(URL url) {
super(url);
}
override lateinit var response: Response
@Override
public int onExecute() throws IOException {
mCall = mOkHttpClient.newCall(mRequest);
mResponse = mCall.execute();
return super.getStatusCode();
public override fun onExecute(): Int {
call = okHttpClient.newCall(request)
call?.let { response = it.execute() }
return super.statusCode
}
}

View File

@ -21,29 +21,26 @@
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.nonwebdav
package com.owncloud.android.lib.common.http.methods.nonwebdav;
import java.io.IOException;
import java.net.URL;
import okhttp3.RequestBody
import java.io.IOException
import java.net.URL
/**
* OkHttp post calls wrapper
*
* @author David González Verdugo
*/
public class PostMethod extends HttpMethod {
public PostMethod(URL url) {
super(url);
}
@Override
public int onExecute() throws IOException {
mRequest = mRequest.newBuilder()
.post(mRequestBody)
.build();
return super.onExecute();
class PostMethod(
url: URL,
private val postRequestBody: RequestBody
) : HttpMethod(url) {
@Throws(IOException::class)
override fun onExecute(): Int {
request = request.newBuilder()
.post(postRequestBody)
.build()
return super.onExecute()
}
}

View File

@ -0,0 +1,46 @@
/* 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.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(): Int {
request = request.newBuilder()
.put(putRequestBody)
.build()
return super.onExecute()
}
}

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.common.http.methods.webdav;
import kotlin.Unit;
import java.net.URL;
/**
* Copy calls wrapper
*
* @author Christian Schabesberger
* @author David González Verdugo
*/
public class CopyMethod extends DavMethod {
final String destinationUrl;
final boolean forceOverride;
public CopyMethod(URL url, String destinationUrl, boolean forceOverride) {
super(url);
this.destinationUrl = destinationUrl;
this.forceOverride = forceOverride;
}
@Override
public int onExecute() throws Exception {
mDavResource.copy(destinationUrl, forceOverride, response -> {
mResponse = response;
return Unit.INSTANCE;
});
return super.getStatusCode();
}
}

View File

@ -0,0 +1,51 @@
/* 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 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,
private val forceOverride: Boolean
) : DavMethod(url) {
@Throws(Exception::class)
public override fun onExecute(): Int {
davResource.copy(
destinationUrl,
forceOverride,
super.getRequestHeadersAsHashMap()
) { callBackResponse: Response ->
response = callBackResponse
}
return super.statusCode
}
}

View File

@ -21,13 +21,12 @@
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav;
package com.owncloud.android.lib.common.http.methods.webdav
/**
* @author David González Verdugo
*/
public class DavConstants {
public static final int DEPTH_0 = 0;
public static final int DEPTH_1 = 1;
object DavConstants {
const val DEPTH_0 = 0
const val DEPTH_1 = 1
}

View File

@ -1,162 +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.dav4android.Constants;
import at.bitfire.dav4android.DavOCResource;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4android.exception.RedirectException;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import okhttp3.HttpUrl;
import okhttp3.Protocol;
import okhttp3.Response;
import okhttp3.ResponseBody;
import java.net.URL;
import java.util.concurrent.TimeUnit;
/**
* Wrapper to perform WebDAV (dav4android) calls
*
* @author David González Verdugo
*/
public abstract class DavMethod extends HttpBaseMethod {
protected DavOCResource mDavResource;
protected DavMethod(URL url) {
super(url);
mDavResource = new DavOCResource(
mOkHttpClient,
HttpUrl.parse(url.toString()),
Constants.INSTANCE.getLog());
}
@Override
public void abort() {
mDavResource.cancelCall();
}
@Override
public int execute() throws Exception {
try {
return onExecute();
} catch (HttpException httpException) {
// Modify responses with information gathered from exceptions
if (httpException instanceof RedirectException) {
mResponse = new Response.Builder()
.header(
HttpConstants.LOCATION_HEADER, ((RedirectException) httpException).getRedirectLocation()
)
.code(httpException.getCode())
.request(mRequest)
.message(httpException.getMessage())
.protocol(Protocol.HTTP_1_1)
.build();
} else if (mResponse != null) {
// 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 (mResponse.body().contentType() != null) {
ResponseBody responseBody = ResponseBody.create(
mResponse.body().contentType(),
httpException.getResponseBody()
);
mResponse = mResponse.newBuilder()
.body(responseBody)
.build();
}
}
return httpException.getCode();
}
}
//////////////////////////////
// Setter
//////////////////////////////
// Connection parameters
@Override
public void setReadTimeout(long readTimeout, TimeUnit timeUnit) {
super.setReadTimeout(readTimeout, timeUnit);
mDavResource = new DavOCResource(
mOkHttpClient,
HttpUrl.parse(mRequest.url().toString()),
Constants.INSTANCE.getLog());
}
@Override
public void setConnectionTimeout(long connectionTimeout, TimeUnit timeUnit) {
super.setConnectionTimeout(connectionTimeout, timeUnit);
mDavResource = new DavOCResource(
mOkHttpClient,
HttpUrl.parse(mRequest.url().toString()),
Constants.INSTANCE.getLog());
}
@Override
public void setFollowRedirects(boolean followRedirects) {
super.setFollowRedirects(followRedirects);
mDavResource = new DavOCResource(
mOkHttpClient,
HttpUrl.parse(mRequest.url().toString()),
Constants.INSTANCE.getLog());
}
@Override
public void setUrl(HttpUrl url) {
super.setUrl(url);
mDavResource = new DavOCResource(
mOkHttpClient,
HttpUrl.parse(mRequest.url().toString()),
Constants.INSTANCE.getLog());
}
@Override
public boolean getRetryOnConnectionFailure() {
return false; //TODO: implement me
}
//////////////////////////////
// Getter
//////////////////////////////
@Override
public void setRetryOnConnectionFailure(boolean retryOnConnectionFailure) {
super.setRetryOnConnectionFailure(retryOnConnectionFailure);
mDavResource = new DavOCResource(
mOkHttpClient,
HttpUrl.parse(mRequest.url().toString()),
Constants.INSTANCE.getLog());
}
@Override
public boolean isAborted() {
return mDavResource.isCallAborted();
}
}

View File

@ -0,0 +1,161 @@
/* 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.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Protocol
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import java.net.MalformedURLException
import java.net.URL
import java.util.concurrent.TimeUnit
/**
* Wrapper to perform WebDAV (dav4android) calls
*
* @author David González Verdugo
*/
abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) {
protected var davResource: DavOCResource
override lateinit var response: Response
init {
val httpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException()
davResource = DavOCResource(
okHttpClient,
httpUrl,
log
)
}
override fun abort() {
davResource.cancelCall()
}
@Throws(Exception::class)
override fun execute(): Int {
return try {
onExecute()
} 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
}
}
//////////////////////////////
// Setter
//////////////////////////////
// Connection parameters
override fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) {
super.setReadTimeout(readTimeout, timeUnit)
davResource = DavOCResource(
okHttpClient,
request.url,
log
)
}
override fun setConnectionTimeout(
connectionTimeout: Long,
timeUnit: TimeUnit
) {
super.setConnectionTimeout(connectionTimeout, timeUnit)
davResource = DavOCResource(
okHttpClient,
request.url,
log
)
}
override fun setFollowRedirects(followRedirects: Boolean) {
super.setFollowRedirects(followRedirects)
davResource = DavOCResource(
okHttpClient,
request.url,
log
)
}
override fun setUrl(url: HttpUrl) {
super.setUrl(url)
davResource = DavOCResource(
okHttpClient,
request.url,
log
)
}
override fun setRequestHeader(name: String, value: String) {
super.setRequestHeader(name, value)
davResource = DavOCResource(
okHttpClient,
request.url,
log
)
}
//////////////////////////////
// Getter
//////////////////////////////
override fun setRetryOnConnectionFailure(retryOnConnectionFailure: Boolean) {
super.setRetryOnConnectionFailure(retryOnConnectionFailure)
davResource = DavOCResource(
okHttpClient,
request.url,
log
)
}
override val isAborted: Boolean
get() = davResource.isCallAborted()
}

View File

@ -1,15 +0,0 @@
package com.owncloud.android.lib.common.http.methods.webdav;
import at.bitfire.dav4android.Property;
import at.bitfire.dav4android.PropertyUtils;
public class DavUtils {
public static final Property.Name[] getAllPropset() {
return PropertyUtils.INSTANCE.getAllPropSet();
}
public static final Property.Name[] getQuotaPropSet() {
return PropertyUtils.INSTANCE.getQuotaPropset();
}
}

View File

@ -21,24 +21,16 @@
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav
package com.owncloud.android.lib.common.http.methods.nonwebdav;
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.PropertyUtils.getAllPropSet
import at.bitfire.dav4jvm.PropertyUtils.getQuotaPropset
import java.io.IOException;
import java.net.URL;
object DavUtils {
@JvmStatic val allPropset: Array<Property.Name>
get() = getAllPropSet()
public class PutMethod extends HttpMethod {
public PutMethod(URL url) {
super(url);
}
@Override
public int onExecute() throws IOException {
mRequest = mRequest.newBuilder()
.put(mRequestBody)
.build();
return super.onExecute();
}
val quotaPropSet: Array<Property.Name>
get() = getQuotaPropset()
}

View File

@ -21,12 +21,10 @@
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav
package com.owncloud.android.lib.common.http.methods.webdav;
import kotlin.Unit;
import java.net.URL;
import okhttp3.Response
import java.net.URL
/**
* MkCol calls wrapper
@ -34,18 +32,15 @@ import java.net.URL;
* @author Christian Schabesberger
* @author David González Verdugo
*/
public class MkColMethod extends DavMethod {
public MkColMethod(URL url) {
super(url);
}
@Override
public int onExecute() throws Exception {
mDavResource.mkCol(null, response -> {
mResponse = response;
return Unit.INSTANCE;
});
return super.getStatusCode();
class MkColMethod(url: URL) : DavMethod(url) {
@Throws(Exception::class)
public override fun onExecute(): Int {
davResource.mkCol(
xmlBody = null,
listOfHeaders = super.getRequestHeadersAsHashMap()
) { callBackResponse: Response ->
response = callBackResponse
}
return super.statusCode
}
}

View File

@ -1,61 +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 com.owncloud.android.lib.common.http.HttpConstants;
import kotlin.Unit;
import java.net.URL;
/**
* Move calls wrapper
*
* @author Christian Schabesberger
* @author David González Verdugo
*/
public class MoveMethod extends DavMethod {
final String destinationUrl;
final boolean forceOverride;
public MoveMethod(URL url, String destinationUrl, boolean forceOverride) {
super(url);
this.destinationUrl = destinationUrl;
this.forceOverride = forceOverride;
}
@Override
public int onExecute() throws Exception {
mDavResource.move(
destinationUrl,
forceOverride,
super.getRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER),
super.getRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER), response -> {
mResponse = response;
return Unit.INSTANCE;
});
return super.getStatusCode();
}
}

View File

@ -0,0 +1,52 @@
/* 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 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,
private val forceOverride: Boolean
) : DavMethod(url) {
@Throws(Exception::class)
public override fun onExecute(): Int {
davResource.move(
destinationUrl,
forceOverride,
super.getRequestHeadersAsHashMap()
) { callBackResponse: Response ->
response = callBackResponse
}
return super.statusCode
}
}

View File

@ -1,94 +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.dav4android.Property;
import at.bitfire.dav4android.Response;
import at.bitfire.dav4android.exception.DavException;
import kotlin.Unit;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* Propfind calls wrapper
*
* @author David González Verdugo
*/
public class PropfindMethod extends DavMethod {
// request
private final int mDepth;
private final Property.Name[] mPropertiesToRequest;
// response
private final List<Response> mMembers;
private Response mRoot;
public PropfindMethod(URL url, int depth, Property.Name[] propertiesToRequest) {
super(url);
mDepth = depth;
mPropertiesToRequest = propertiesToRequest;
mMembers = new ArrayList<>();
mRoot = null;
}
@Override
public int onExecute() throws IOException, DavException {
mDavResource.propfind(mDepth, mPropertiesToRequest,
(Response response, Response.HrefRelation hrefRelation) -> {
switch (hrefRelation) {
case MEMBER:
mMembers.add(response);
break;
case SELF:
mRoot = response;
break;
case OTHER:
default:
}
return Unit.INSTANCE;
}, response -> {
mResponse = response;
return Unit.INSTANCE;
});
return getStatusCode();
}
public int getDepth() {
return mDepth;
}
public List<Response> getMembers() {
return mMembers;
}
public Response getRoot() {
return mRoot;
}
}

View File

@ -0,0 +1,72 @@
/* 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.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 onExecute(): 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,61 +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.dav4android.exception.HttpException;
import com.owncloud.android.lib.common.http.HttpConstants;
import kotlin.Unit;
import java.io.IOException;
import java.net.URL;
/**
* Put calls wrapper
*
* @author David González Verdugo
*/
public class PutMethod extends DavMethod {
public PutMethod(URL url) {
super(url);
}
;
@Override
public int onExecute() throws IOException, HttpException {
mDavResource.put(
mRequestBody,
super.getRequestHeader(HttpConstants.IF_MATCH_HEADER),
super.getRequestHeader(HttpConstants.CONTENT_TYPE_HEADER),
super.getRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER),
super.getRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER), response -> {
mResponse = response;
return Unit.INSTANCE;
});
return super.getStatusCode();
}
}

View File

@ -0,0 +1,52 @@
/* 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.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 onExecute(): Int {
davResource.put(
putRequestBody,
super.getRequestHeader(HttpConstants.IF_MATCH_HEADER),
getRequestHeadersAsHashMap()
) { callBackResponse ->
response = callBackResponse
}
return super.statusCode
}
}

View File

@ -45,6 +45,7 @@ public class AdvancedX509TrustManager implements X509TrustManager {
private X509TrustManager mStandardTrustManager;
private KeyStore mKnownServersKeyStore;
/**
* Constructor for AdvancedX509TrustManager
*
@ -66,7 +67,7 @@ public class AdvancedX509TrustManager implements X509TrustManager {
* @return The first X509TrustManager found in factory.
*/
private X509TrustManager findX509TrustManager(TrustManagerFactory factory) {
TrustManager tms[] = factory.getTrustManagers();
TrustManager[] tms = factory.getTrustManagers();
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager) tm;

View File

@ -44,9 +44,9 @@ import java.util.Set;
*/
public class FileRequestBody extends RequestBody implements ProgressiveDataTransferer {
final Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
protected File mFile;
private MediaType mContentType;
final Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
public FileRequestBody(File file, MediaType contentType) {
mFile = file;

View File

@ -34,9 +34,6 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import static com.owncloud.android.lib.common.OwnCloudClient.WEBDAV_FILES_PATH_4_0;
import static com.owncloud.android.lib.common.OwnCloudClient.WEBDAV_PATH_4_0_AND_LATER;
public class WebdavUtils {
private static final SimpleDateFormat[] DATETIME_FORMATS = {

View File

@ -27,8 +27,8 @@ package com.owncloud.android.lib.common.operations;
import android.accounts.Account;
import android.accounts.AccountsException;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
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;

View File

@ -25,7 +25,7 @@ package com.owncloud.android.lib.resources.files
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
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.RedirectionPath
import com.owncloud.android.lib.common.network.WebdavUtils
@ -66,7 +66,7 @@ class CheckPathExistenceRemoteOperation(
if (isUserLogged) client.baseFilesWebDavUri.toString()
else client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)
val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.getAllPropset()).apply {
val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropset).apply {
setReadTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
}

View File

@ -29,12 +29,11 @@ import timber.log.Timber;
import java.io.File;
public class FileUtils {
public static final String PATH_SEPARATOR = "/";
public static final String FINAL_CHUNKS_FILE = ".file";
static String getParentPath(String remotePath) {
String parentPath = new File(remotePath).getParent();
parentPath = parentPath.endsWith(PATH_SEPARATOR) ? parentPath : parentPath + PATH_SEPARATOR;
parentPath = parentPath.endsWith(File.separator) ? parentPath : parentPath + File.separator;
return parentPath;
}
@ -42,14 +41,12 @@ public class FileUtils {
* Validate the fileName to detect if contains any forbidden character: / , \ , < , > ,
* : , " , | , ? , *
*
* @param fileName
* @return
*/
public static boolean isValidName(String fileName) {
boolean result = true;
Timber.d("fileName =======%s", fileName);
if (fileName.contains(PATH_SEPARATOR)) {
if (fileName.contains(File.separator)) {
result = false;
}
return result;

View File

@ -24,7 +24,7 @@
package com.owncloud.android.lib.resources.files;
import at.bitfire.dav4android.Response;
import at.bitfire.dav4jvm.Response;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.http.HttpConstants;
@ -122,7 +122,6 @@ public class ReadRemoteFolderOperation extends RemoteOperation<ArrayList<RemoteF
}
private boolean isSuccess(int status) {
return status == HttpConstants.HTTP_MULTI_STATUS ||
status == HttpConstants.HTTP_OK;
return status == HttpConstants.HTTP_MULTI_STATUS || status == HttpConstants.HTTP_OK;
}
}

View File

@ -27,20 +27,11 @@ package com.owncloud.android.lib.resources.files;
import android.os.Parcel;
import android.os.Parcelable;
import at.bitfire.dav4android.Property;
import at.bitfire.dav4android.Response;
import at.bitfire.dav4android.property.CreationDate;
import at.bitfire.dav4android.property.GetContentLength;
import at.bitfire.dav4android.property.GetContentType;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.dav4android.property.GetLastModified;
import at.bitfire.dav4android.property.QuotaAvailableBytes;
import at.bitfire.dav4android.property.QuotaUsedBytes;
import at.bitfire.dav4android.property.owncloud.OCId;
import at.bitfire.dav4android.property.owncloud.OCPermissions;
import at.bitfire.dav4android.property.owncloud.OCPrivatelink;
import at.bitfire.dav4android.property.owncloud.OCSize;
import at.bitfire.dav4jvm.Property;
import at.bitfire.dav4jvm.Response;
import at.bitfire.dav4jvm.property.*;
import java.io.File;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@ -92,13 +83,13 @@ public class RemoteFile implements Parcelable, Serializable {
/**
* Create new {@link RemoteFile} with given path.
* <p>
* The path received must be URL-decoded. Path separator must be OCFile.PATH_SEPARATOR, and it must be the first character in 'path'.
* The path received must be URL-decoded. Path separator must be File.separator, and it must be the first character in 'path'.
*
* @param path The remote path of the file.
*/
public RemoteFile(String path) {
resetData();
if (path == null || path.length() <= 0 || !path.startsWith(FileUtils.PATH_SEPARATOR)) {
if (path == null || path.length() <= 0 || !path.startsWith(File.separator)) {
throw new IllegalArgumentException("Trying to create a OCFile with a non valid remote path: " + path);
}
mRemotePath = path;

View File

@ -41,7 +41,7 @@ class RemoteFileUtil {
*/
fun getRemotePathFromUrl(url: HttpUrl, userId: String): String? {
val davFilesPath = OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId
val absoluteDavPath = Uri.decode(url.encodedPath())
val absoluteDavPath = Uri.decode(url.encodedPath)
val pathToOc = absoluteDavPath.split(davFilesPath)[0]
return absoluteDavPath.replace(pathToOc + davFilesPath, "")
}

View File

@ -68,11 +68,10 @@ public class RenameRemoteFileOperation extends RemoteOperation {
mNewName = newName;
String parent = (new File(mOldRemotePath)).getParent();
parent = (parent.endsWith(FileUtils.PATH_SEPARATOR)) ? parent : parent +
FileUtils.PATH_SEPARATOR;
parent = (parent.endsWith(File.separator)) ? parent : parent + File.separator;
mNewRemotePath = parent + mNewName;
if (isFolder) {
mNewRemotePath += FileUtils.PATH_SEPARATOR;
mNewRemotePath += File.separator;
}
}

View File

@ -84,10 +84,6 @@ public class UploadRemoteFileOperation extends RemoteOperation {
RemoteOperationResult result;
try {
mPutMethod = new PutMethod(
new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)));
mPutMethod.setRetryOnConnectionFailure(false);
if (mCancellationRequested.get()) {
// the operation was cancelled before getting it's turn to be executed in the queue of uploads
@ -125,6 +121,11 @@ public class UploadRemoteFileOperation extends RemoteOperation {
mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners);
}
mPutMethod = new PutMethod(
new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), mFileRequestBody);
mPutMethod.setRetryOnConnectionFailure(false);
if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
mPutMethod.addRequestHeader(HttpConstants.IF_MATCH_HEADER, mRequiredEtag);
}
@ -132,8 +133,6 @@ public class UploadRemoteFileOperation extends RemoteOperation {
mPutMethod.addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, String.valueOf(fileToUpload.length()));
mPutMethod.addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, mFileLastModifTimestamp);
mPutMethod.setRequestBody(mFileRequestBody);
int status = client.executeHttpMethod(mPutMethod);
if (isSuccess(status)) {

View File

@ -29,7 +29,6 @@ import com.owncloud.android.lib.common.http.methods.webdav.PutMethod;
import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody;
import com.owncloud.android.lib.common.operations.OperationCancelledException;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.files.FileUtils;
import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
import okhttp3.MediaType;
import timber.log.Timber;
@ -82,18 +81,11 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation
}
long offset = 0;
String uriPrefix = client.getUploadsWebDavUri() + FileUtils.PATH_SEPARATOR + String.valueOf(mTransferId);
String uriPrefix = client.getUploadsWebDavUri() + File.separator + mTransferId;
long totalLength = fileToUpload.length();
long chunkCount = (long) Math.ceil((double) totalLength / CHUNK_SIZE);
for (int chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++, offset += CHUNK_SIZE) {
mPutMethod = new PutMethod(
new URL(uriPrefix + FileUtils.PATH_SEPARATOR + chunkIndex)
);
if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
mPutMethod.addRequestHeader(IF_MATCH_HEADER, "\"" + mRequiredEtag + "\"");
}
((ChunkFromFileRequestBody) mFileRequestBody).setOffset(offset);
@ -101,6 +93,12 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation
result = new RemoteOperationResult<>(new OperationCancelledException());
break;
} else {
mPutMethod = new PutMethod(new URL(uriPrefix + File.separator + chunkIndex), mFileRequestBody);
if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
mPutMethod.addRequestHeader(IF_MATCH_HEADER, "\"" + mRequiredEtag + "\"");
}
if (chunkIndex == chunkCount - 1) {
// Added a high timeout to the last chunk due to when the last chunk
// arrives to the server with the last PUT, all chunks get assembled
@ -108,8 +106,6 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation
mPutMethod.setReadTimeout(LAST_CHUNK_TIMEOUT, TimeUnit.MILLISECONDS);
}
mPutMethod.setRequestBody(mFileRequestBody);
status = client.executeHttpMethod(mPutMethod);
Timber.d("Upload of " + mLocalPath + " to " + mRemotePath +

View File

@ -116,9 +116,7 @@ class CreateRemoteShareOperation(
val uriBuilder = requestUri.buildUpon()
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH)
val postMethod = PostMethod(URL(uriBuilder.build().toString()))
postMethod.setRequestBody(formBodyBuilder.build())
val postMethod = PostMethod(URL(uriBuilder.build().toString()), formBodyBuilder.build())
postMethod.setRequestHeader(HttpConstants.CONTENT_TYPE_HEADER, HttpConstants.CONTENT_TYPE_URLENCODED_UTF8)
postMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
@ -133,7 +131,7 @@ class CreateRemoteShareOperation(
parser.oneOrMoreSharesRequired = true
parser.ownCloudVersion = client.ownCloudVersion
parser.serverBaseUri = client.baseUri
result = parser.parse(postMethod.responseBodyAsString)
result = parser.parse(postMethod.getResponseBodyAsString())
if (result.isSuccess && retrieveShareDetails) {
// retrieve more info - POST only returns the index of the new share
@ -145,7 +143,7 @@ class CreateRemoteShareOperation(
}
} else {
result = parser.parse(postMethod.responseBodyAsString)
result = parser.parse(postMethod.getResponseBodyAsString())
}
} catch (e: Exception) {

View File

@ -98,7 +98,7 @@ class GetRemoteShareesOperation
getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
val status = client.executeHttpMethod(getMethod)
val response = getMethod.responseBodyAsString
val response = getMethod.getResponseBodyAsString()
if (isSuccess(status)) {
Timber.d("Successful response: $response")

View File

@ -86,10 +86,10 @@ class GetRemoteSharesForFileOperation(
)
parser.ownCloudVersion = client.ownCloudVersion
parser.serverBaseUri = client.baseUri
result = parser.parse(getMethod.responseBodyAsString)
result = parser.parse(getMethod.getResponseBodyAsString())
if (result.isSuccess) {
Timber.d("Got " + result.data.shares.size + " shares")
Timber.d("Got ${result.data.shares.size} shares")
}
} else {
result = RemoteOperationResult(getMethod)

View File

@ -25,6 +25,7 @@
package com.owncloud.android.lib.resources.shares
import com.owncloud.android.lib.resources.files.FileUtils
import java.io.File
/**
* Contains the data of a Share from the Share API
@ -48,7 +49,7 @@ data class RemoteShare(
var permissions: Int = DEFAULT_PERMISSION,
var sharedDate: Long = INIT_SHARED_DATE,
var expirationDate: Long = INIT_EXPIRATION_DATE_IN_MILLIS,
var isFolder: Boolean = path.endsWith(FileUtils.PATH_SEPARATOR),
var isFolder: Boolean = path.endsWith(File.separator),
var userId: Long = 0,
val isValid: Boolean = id > -1
) {

View File

@ -73,9 +73,9 @@ class RemoveRemoteShareOperation(private val remoteShareId: Long) : RemoteOperat
val parser = ShareToRemoteOperationResultParser(
ShareXMLParser()
)
result = parser.parse(deleteMethod.responseBodyAsString)
result = parser.parse(deleteMethod.getResponseBodyAsString())
Timber.d("Unshare " + remoteShareId + ": " + result.logMessage)
Timber.d("Unshare $remoteShareId: ${result.logMessage}")
} else {
result = RemoteOperationResult(deleteMethod)
@ -83,7 +83,7 @@ class RemoveRemoteShareOperation(private val remoteShareId: Long) : RemoteOperat
} catch (e: Exception) {
result = RemoteOperationResult(e)
Timber.e(e, "Unshare Link Exception " + result.logMessage)
Timber.e(e, "Unshare Link Exception ${result.logMessage}")
}
return result

View File

@ -25,14 +25,11 @@
package com.owncloud.android.lib.resources.shares
import android.util.Xml
import com.owncloud.android.lib.common.network.WebdavUtils
import com.owncloud.android.lib.resources.files.FileUtils
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
import org.xmlpull.v1.XmlPullParserFactory
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.util.ArrayList
@ -330,9 +327,9 @@ class ShareXMLParser {
private fun fixPathForFolder(share: RemoteShare) {
if (share.isFolder && share.path.isNotEmpty() &&
!share.path.endsWith(FileUtils.PATH_SEPARATOR)
!share.path.endsWith(File.separator)
) {
share.path = share.path + FileUtils.PATH_SEPARATOR
share.path = share.path + File.separator
}
}

View File

@ -143,9 +143,7 @@ class UpdateRemoteShareOperation
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH)
uriBuilder.appendEncodedPath(remoteId.toString())
val putMethod = PutMethod(URL(uriBuilder.build().toString()))
putMethod.setRequestBody(formBodyBuilder.build())
val putMethod = PutMethod(URL(uriBuilder.build().toString()), formBodyBuilder.build())
putMethod.setRequestHeader(HttpConstants.CONTENT_TYPE_HEADER, HttpConstants.CONTENT_TYPE_URLENCODED_UTF8)
putMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
@ -158,12 +156,12 @@ class UpdateRemoteShareOperation
)
if (!isSuccess(status)) {
return parser.parse(putMethod.responseBodyAsString)
return parser.parse(putMethod.getResponseBodyAsString())
}
parser.ownCloudVersion = client.ownCloudVersion
parser.serverBaseUri = client.baseUri
result = parser.parse(putMethod.responseBodyAsString)
result = parser.parse(putMethod.getResponseBodyAsString())
if (result.isSuccess && retrieveShareDetails) {
// retrieve more info - PUT only returns the index of the new share

View File

@ -65,7 +65,7 @@ class GetRemoteCapabilitiesOperation : RemoteOperation<RemoteCapability>() {
}
val status = client.executeHttpMethod(getMethod)
val response = getMethod.responseBodyAsString
val response = getMethod.getResponseBodyAsString()
if (status == HttpConstants.HTTP_OK) {
Timber.d("Successful response $response")

View File

@ -108,7 +108,7 @@ class GetRemoteStatusOperation : RemoteOperation<OwnCloudVersion>() {
}
if (isSuccess(status)) {
val respJSON = JSONObject(getMethod.responseBodyAsString)
val respJSON = JSONObject(getMethod.getResponseBodyAsString())
if (!respJSON.getBoolean(NODE_INSTALLED)) {
latestResult = RemoteOperationResult(ResultCode.INSTANCE_NOT_CONFIGURED)
} else {

View File

@ -38,6 +38,8 @@ class OwnCloudVersion(version: String) : Comparable<OwnCloudVersion>, Parcelable
var isVersionValid: Boolean = false
set
var isVersionHidden: Boolean = false
val version: String
get() = if (isVersionValid) {
toString()
@ -55,6 +57,7 @@ class OwnCloudVersion(version: String) : Comparable<OwnCloudVersion>, Parcelable
var versionToParse = version
mVersion = 0
isVersionValid = false
isVersionHidden = version.isBlank()
val countDots = versionToParse.length - versionToParse.replace(".", "").length
// Complete the version. Version must have 3 dots

View File

@ -21,6 +21,7 @@ package com.owncloud.android.lib.resources.status.services.implementation
import android.net.Uri
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.getAnonymousCredentials
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.status.services.ServerInfoService
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
@ -38,6 +39,6 @@ class OCServerInfoService : ServerInfoService {
GetRemoteStatusOperation().execute(createClientFromPath(path))
private fun createClientFromPath(path: String): OwnCloudClient {
return OwnCloudClient(Uri.parse(path))
return OwnCloudClient(Uri.parse(path)).apply { credentials = getAnonymousCredentials() }
}
}

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.users;
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.WebdavUtils;
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.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Gets avatar about the user logged in, if available
*
* @author David A. Velasco
* @author David González Verdugo
*/
public class GetRemoteUserAvatarOperation extends RemoteOperation<GetRemoteUserAvatarOperation.ResultData> {
private static final String NON_OFFICIAL_AVATAR_PATH = "/index.php/avatar/";
/**
* Desired size in pixels of the squared image
*/
private int mDimension;
public GetRemoteUserAvatarOperation(int dimension) {
mDimension = dimension;
}
@Override
protected RemoteOperationResult<ResultData> run(OwnCloudClient client) {
GetMethod getMethod = null;
RemoteOperationResult<ResultData> result;
InputStream inputStream = null;
BufferedInputStream bis = null;
ByteArrayOutputStream bos = null;
try {
final String url = client.getBaseUri() + NON_OFFICIAL_AVATAR_PATH + client.getCredentials().getUsername() + "/" + mDimension;
Timber.d("avatar URI: %s", url);
getMethod = new GetMethod(new URL(url));
int status = client.executeHttpMethod(getMethod);
if (isSuccess(status)) {
// find out size of file to read
int totalToTransfer = 0;
String contentLength = getMethod.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER);
if (contentLength != null && contentLength.length() > 0) {
totalToTransfer = Integer.parseInt(contentLength);
}
// find out MIME-type!
String mimeType;
String contentType = getMethod.getResponseHeader(HttpConstants.CONTENT_TYPE_HEADER);
if (contentType == null || !contentType.startsWith("image")) {
Timber.w("Not an image, failing with no avatar");
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.FILE_NOT_FOUND);
return result;
}
mimeType = contentType;
/// download will be performed to a buffer
inputStream = getMethod.getResponseBodyAsStream();
bis = new BufferedInputStream(inputStream);
bos = new ByteArrayOutputStream(totalToTransfer);
byte[] bytes = new byte[4096];
int readResult;
while ((readResult = bis.read(bytes)) != -1) {
bos.write(bytes, 0, readResult);
}
// TODO check total bytes transferred?
// find out etag
String etag = WebdavUtils.getEtagFromResponse(getMethod);
if (etag.length() == 0) {
Timber.w("Could not read Etag from avatar");
}
// Result
result = new RemoteOperationResult<>(OK);
result.setData(new ResultData(bos.toByteArray(), mimeType, etag));
} else {
result = new RemoteOperationResult<>(getMethod);
client.exhaustResponse(getMethod.getResponseBodyAsStream());
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Timber.e(e, "Exception while getting OC user avatar");
} finally {
if (getMethod != null) {
try {
if (inputStream != null) {
client.exhaustResponse(inputStream);
if (bis != null) {
bis.close();
} else {
inputStream.close();
}
}
} catch (IOException i) {
Timber.e(i, "Unexpected exception closing input stream");
}
try {
if (bos != null) {
bos.close();
}
} catch (IOException o) {
Timber.e(o, "Unexpected exception closing output stream");
}
}
}
return result;
}
private boolean isSuccess(int status) {
return (status == HttpConstants.HTTP_OK);
}
public static class ResultData {
private String mEtag;
private String mMimeType;
private byte[] mAvatarData;
ResultData(byte[] avatarData, String mimeType, String etag) {
mAvatarData = avatarData;
mMimeType = (mimeType == null) ? "" : mimeType;
mEtag = (etag == null) ? "" : etag;
}
public String getEtag() {
return mEtag;
}
public String getMimeType() {
return mMimeType;
}
public byte[] getAvatarData() {
return mAvatarData;
}
}
}

View File

@ -0,0 +1,114 @@
/* 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.users
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.WebdavUtils
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.net.URL
/**
* Gets avatar about the user logged in, if available
*
* @author David A. Velasco
* @author David González Verdugo
*/
class GetRemoteUserAvatarOperation(private val avatarDimension: Int) : RemoteOperation<RemoteAvatarData>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteAvatarData> {
var inputStream: InputStream? = null
var result: RemoteOperationResult<RemoteAvatarData>
try {
val endPoint =
client.baseUri.toString() + NON_OFFICIAL_AVATAR_PATH + client.credentials.username + File.separator + avatarDimension
Timber.d("avatar URI: %s", endPoint)
val getMethod = GetMethod(URL(endPoint))
val status = client.executeHttpMethod(getMethod)
if (isSuccess(status)) {
// find out size of file to read
val contentLength = getMethod.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER)?.toInt()
// find out MIME-type!
val mimeType = getMethod.getResponseHeader(HttpConstants.CONTENT_TYPE_HEADER)
if (mimeType == null || !mimeType.startsWith("image")) {
Timber.w("Not an image, failing with no avatar")
return RemoteOperationResult(RemoteOperationResult.ResultCode.FILE_NOT_FOUND)
}
/// download will be performed to a buffer
inputStream = getMethod.getResponseBodyAsStream()
val bytesArray = inputStream?.readBytes()?: byteArrayOf()
// TODO check total bytes transferred?
Timber.d("Avatar size: Bytes received ${bytesArray.size} of $contentLength")
// find out etag
val etag = WebdavUtils.getEtagFromResponse(getMethod)
if (etag.isEmpty()) {
Timber.w("Could not read Etag from avatar")
}
// Result
result = RemoteOperationResult(RemoteOperationResult.ResultCode.OK)
result.setData(RemoteAvatarData(bytesArray, mimeType, etag))
} else {
result = RemoteOperationResult(getMethod)
client.exhaustResponse(getMethod.getResponseBodyAsStream())
}
} catch (e: Exception) {
result = RemoteOperationResult(e)
Timber.e(e, "Exception while getting OC user avatar")
} finally {
try {
client.exhaustResponse(inputStream)
inputStream?.close()
} catch (i: IOException) {
Timber.e(i, "Unexpected exception closing input stream")
}
}
return result
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
companion object {
private const val NON_OFFICIAL_AVATAR_PATH = "/index.php/avatar/"
}
}

View File

@ -53,7 +53,7 @@ class GetRemoteUserInfoOperation : RemoteOperation<RemoteUserInfo>() {
try {
val getMethod = GetMethod(URL(client.baseUri.toString() + OCS_ROUTE))
val status = client.executeHttpMethod(getMethod)
val response = getMethod.responseBodyAsString
val response = getMethod.getResponseBodyAsString() ?: ""
if (status == HttpConstants.HTTP_OK) {
Timber.d("Successful response $response")

View File

@ -1,185 +0,0 @@
/* ownCloud Android Library is available under MIT license
*
* Copyright (C) 2020 ownCloud Inc.
* Copyright (C) 2015 Bartosz Przybylski
* Copyright (C) 2014 Marcello Steiner
*
* 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.users;
import at.bitfire.dav4android.Property;
import at.bitfire.dav4android.property.QuotaAvailableBytes;
import at.bitfire.dav4android.property.QuotaUsedBytes;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils;
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import timber.log.Timber;
import java.net.URL;
import java.util.List;
import static com.owncloud.android.lib.common.http.methods.webdav.DavConstants.DEPTH_0;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* @author marcello
* @author David González Verdugo
*/
public class GetRemoteUserQuotaOperation extends RemoteOperation<GetRemoteUserQuotaOperation.RemoteQuota> {
private String mRemotePath;
/**
* Constructor
*
* @param remotePath Remote path of the file.
*/
public GetRemoteUserQuotaOperation(String remotePath) {
mRemotePath = remotePath;
}
@Override
protected RemoteOperationResult<RemoteQuota> run(OwnCloudClient client) {
RemoteOperationResult<RemoteQuota> result = null;
try {
PropfindMethod propfindMethod = new PropfindMethod(
new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
DEPTH_0,
DavUtils.getQuotaPropSet());
int status = client.executeHttpMethod(propfindMethod);
if (isSuccess(status)) {
RemoteQuota remoteQuota = readData(propfindMethod.getRoot().getProperties());
result = new RemoteOperationResult<>(OK);
// Add data to the result
if (result.isSuccess()) {
result.setData(remoteQuota);
}
} else { // synchronization failed
result = new RemoteOperationResult<>(propfindMethod);
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
} finally {
if (result.isSuccess()) {
Timber.i("Get quota from " + mRemotePath + ": " + result.getLogMessage());
} else {
if (result.isException()) {
Timber.e(result.getException(), "Get quota from " + mRemotePath + ": " + result.getLogMessage());
} else {
Timber.e("Get quota from " + mRemotePath + ": " + result.getLogMessage());
}
}
}
return result;
}
private boolean isSuccess(int status) {
return status == HttpConstants.HTTP_MULTI_STATUS || status == HttpConstants.HTTP_OK;
}
/**
* Read the data retrieved from the server about the quota
*
* @param properties WebDAV properties containing quota data
* @return new {@link RemoteQuota} instance representing the data read from the server
*/
private RemoteQuota readData(List<Property> properties) {
long quotaAvailable = 0;
long quotaUsed = 0;
for (Property property : properties) {
if (property instanceof QuotaAvailableBytes) {
quotaAvailable = ((QuotaAvailableBytes) property).getQuotaAvailableBytes();
}
if (property instanceof QuotaUsedBytes) {
quotaUsed = ((QuotaUsedBytes) property).getQuotaUsedBytes();
}
}
// If there's a special case, quota available will contain a negative code
// -1, PENDING: Not computed yet, e.g. external storage mounted but folder sizes need scanning
// -2, UNKNOWN: Storage not accessible, e.g. external storage with no API to ask for the free space
// -3, UNLIMITED: Quota using all the storage
if (quotaAvailable < 0) {
return new RemoteQuota(
quotaAvailable,
quotaUsed,
0,
0
);
} else {
long totalQuota = quotaAvailable + quotaUsed;
double relativeQuota = (double) (quotaUsed * 100) / totalQuota;
double roundedRelativeQuota = Math.round(relativeQuota * 100) / 100.0d;
return new RemoteQuota(
quotaAvailable,
quotaUsed,
totalQuota,
roundedRelativeQuota
);
}
}
static public class RemoteQuota {
long mFree, mUsed, mTotal;
double mRelative;
public RemoteQuota(long free, long used, long total, double relative) {
mFree = free;
mUsed = used;
mTotal = total;
mRelative = relative;
}
public long getFree() {
return mFree;
}
public long getUsed() {
return mUsed;
}
public long getTotal() {
return mTotal;
}
public double getRelative() {
return mRelative;
}
}
}

View File

@ -0,0 +1,126 @@
/* ownCloud Android Library is available under MIT license
*
* Copyright (C) 2020 ownCloud Inc.
* Copyright (C) 2015 Bartosz Przybylski
* Copyright (C) 2014 Marcello Steiner
*
* 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.users
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.property.QuotaAvailableBytes
import at.bitfire.dav4jvm.property.QuotaUsedBytes
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.webdav.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.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.lib.resources.users.GetRemoteUserQuotaOperation.RemoteQuota
import timber.log.Timber
import java.net.URL
import kotlin.math.roundToLong
/**
* @author marcello
* @author David González Verdugo
*/
class GetRemoteUserQuotaOperation : RemoteOperation<RemoteQuota>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteQuota> =
try {
val propfindMethod = PropfindMethod(
URL(client.userFilesWebDavUri.toString()),
DavConstants.DEPTH_0,
DavUtils.quotaPropSet
)
with(client.executeHttpMethod(propfindMethod)) {
if (isSuccess(this)) {
RemoteOperationResult<RemoteQuota>(ResultCode.OK).apply {
data = readData(propfindMethod.root?.properties)
}.also {
Timber.i("Get quota completed: ${it.data} and message: ${it.logMessage}")
}
} else { // synchronization failed
RemoteOperationResult<RemoteQuota>(propfindMethod).also {
Timber.e("Get quota without success: ${it.logMessage}")
}
}
}
} catch (e: Exception) {
RemoteOperationResult<RemoteQuota>(e).also {
Timber.e(it.exception, "Get quota: ${it.logMessage}")
}
}
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_MULTI_STATUS || status == HttpConstants.HTTP_OK
/**
* Read the data retrieved from the server about the quota
*
* @param properties WebDAV properties containing quota data
* @return new [RemoteQuota] instance representing the data read from the server
*/
private fun readData(properties: List<Property>?): RemoteQuota {
var quotaAvailable: Long = 0
var quotaUsed: Long = 0
if (properties == null) {
// Should not happen
Timber.d("Unable to get quota")
return RemoteQuota(0, 0, 0, 0.0)
}
for (property in properties) {
if (property is QuotaAvailableBytes) {
quotaAvailable = property.quotaAvailableBytes
}
if (property is QuotaUsedBytes) {
quotaUsed = property.quotaUsedBytes
}
}
Timber.d("Quota used: $quotaUsed, QuotaAvailable: $quotaAvailable")
// If there's a special case, quota available will contain a negative code
// -1, PENDING: Not computed yet, e.g. external storage mounted but folder sizes need scanning
// -2, UNKNOWN: Storage not accessible, e.g. external storage with no API to ask for the free space
// -3, UNLIMITED: Quota using all the storage
return if (quotaAvailable < 0) {
RemoteQuota(
free = quotaAvailable,
used = quotaUsed,
total = 0,
relative = 0.0
)
} else {
val totalQuota = quotaAvailable + quotaUsed
val roundedRelativeQuota = if (totalQuota > 0) {
val relativeQuota = (quotaUsed * 100).toDouble() / totalQuota
(relativeQuota * 100).roundToLong() / 100.0
} else 0.0
RemoteQuota(quotaAvailable, quotaUsed, totalQuota, roundedRelativeQuota)
}
}
data class RemoteQuota(var free: Long, var used: Long, var total: Long, var relative: Double)
}

View File

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

View File

@ -26,8 +26,12 @@ package com.owncloud.android.lib.resources.users.services
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.Service
import com.owncloud.android.lib.resources.users.GetRemoteUserQuotaOperation
import com.owncloud.android.lib.resources.users.RemoteAvatarData
import com.owncloud.android.lib.resources.users.RemoteUserInfo
interface UserService: Service {
fun getUserInfo() : RemoteOperationResult<RemoteUserInfo>
interface UserService : Service {
fun getUserInfo(): RemoteOperationResult<RemoteUserInfo>
fun getUserQuota(): RemoteOperationResult<GetRemoteUserQuotaOperation.RemoteQuota>
fun getUserAvatar(avatarDimension: Int): RemoteOperationResult<RemoteAvatarData>
}

View File

@ -21,12 +21,21 @@ package com.owncloud.android.lib.resources.users.services.implementation
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.users.GetRemoteUserAvatarOperation
import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation
import com.owncloud.android.lib.resources.users.GetRemoteUserQuotaOperation
import com.owncloud.android.lib.resources.users.RemoteAvatarData
import com.owncloud.android.lib.resources.users.RemoteUserInfo
import com.owncloud.android.lib.resources.users.services.UserService
class OCUserService(override val client: OwnCloudClient) :
UserService {
class OCUserService(override val client: OwnCloudClient) : UserService {
override fun getUserInfo(): RemoteOperationResult<RemoteUserInfo> =
GetRemoteUserInfoOperation().execute(client)
override fun getUserQuota(): RemoteOperationResult<GetRemoteUserQuotaOperation.RemoteQuota> =
GetRemoteUserQuotaOperation().execute(client)
override fun getUserAvatar(avatarDimension: Int): RemoteOperationResult<RemoteAvatarData> =
GetRemoteUserAvatarOperation(avatarDimension).execute(client)
}

View File

@ -5,11 +5,11 @@ dependencies {
}
android {
compileSdkVersion 28
compileSdkVersion 29
defaultConfig {
minSdkVersion 21
targetSdkVersion 28
targetSdkVersion 29
// This is pretty ugly but manifest placeholders don't seem to work very well when using different modules
// See https://github.com/openid/AppAuth-Android/issues/325

View File

@ -47,7 +47,6 @@ import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation;
import com.owncloud.android.lib.resources.files.FileUtils;
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
import com.owncloud.android.lib.resources.files.RemoteFile;
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation;
@ -151,14 +150,14 @@ public class MainActivity extends Activity implements OnRemoteOperationListener,
}
private void startRefresh() {
ReadRemoteFolderOperation refreshOperation = new ReadRemoteFolderOperation(FileUtils.PATH_SEPARATOR);
ReadRemoteFolderOperation refreshOperation = new ReadRemoteFolderOperation(File.separator);
refreshOperation.execute(mClient, this, mHandler);
}
private void startUpload() {
File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path));
File fileToUpload = upFolder.listFiles()[0];
String remotePath = FileUtils.PATH_SEPARATOR + fileToUpload.getName();
String remotePath = File.separator + fileToUpload.getName();
String mimeType = getString(R.string.sample_file_mimetype);
// Get the last modification date of the file from the file system
@ -174,7 +173,7 @@ public class MainActivity extends Activity implements OnRemoteOperationListener,
private void startRemoteDeletion() {
File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path));
File fileToUpload = upFolder.listFiles()[0];
String remotePath = FileUtils.PATH_SEPARATOR + fileToUpload.getName();
String remotePath = File.separator + fileToUpload.getName();
RemoveRemoteFileOperation removeOperation = new RemoveRemoteFileOperation(remotePath);
removeOperation.execute(mClient, this, mHandler);
@ -185,7 +184,7 @@ public class MainActivity extends Activity implements OnRemoteOperationListener,
downFolder.mkdir();
File upFolder = new File(getCacheDir(), getString(R.string.upload_folder_path));
File fileToUpload = upFolder.listFiles()[0];
String remotePath = FileUtils.PATH_SEPARATOR + fileToUpload.getName();
String remotePath = File.separator + fileToUpload.getName();
DownloadRemoteFileOperation downloadOperation = new DownloadRemoteFileOperation(remotePath,
downFolder.getAbsolutePath());