1
0
mirror of https://github.com/owncloud/android-library.git synced 2025-07-27 11:46:57 +00:00

Merge pull request #289 from owncloud/master

1.0.4 stable
This commit is contained in:
David González Verdugo 2019-12-17 17:24:16 +01:00 committed by GitHub
commit 85d14d1932
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 465 additions and 1040 deletions

View File

@ -9,7 +9,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:3.5.2'
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-5.6.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

View File

@ -1,13 +1,13 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-allopen'
dependencies {
api 'com.squareup.okhttp3:okhttp:3.12.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
api 'com.gitlab.ownclouders:dav4android:oc_support_1.0.1'
api 'com.github.hannesa2:Logcat:1.5.6'
}
allOpen {
@ -16,14 +16,14 @@ allOpen {
}
android {
compileSdkVersion 29
compileSdkVersion 28
defaultConfig {
minSdkVersion 19
targetSdkVersion 28
versionCode = 10000300
versionName = "1.0.3"
versionCode = 10000400
versionName = "1.0.4"
}
lintOptions {

View File

@ -1,31 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.owncloud.android.lib.testing
/**
* This annotation allows us to open some classes for mocking purposes while they are final in
* release builds.
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class OpenClass
/**
* Annotate a class with [OpenForTesting] if you want it to be extendable in debug builds.
*/
@OpenClass
@Target(AnnotationTarget.CLASS)
annotation class OpenForTesting

View File

@ -11,7 +11,7 @@ import java.io.IOException;
/**
* Dynamic implementation of {@link OwnCloudClientManager}.
* <p>
*
* Wraps instances of {@link SingleSessionManager} and {@link SimpleFactoryManager} and delegates on one
* or the other depending on the known version of the server corresponding to the {@link OwnCloudAccount}
*
@ -60,4 +60,4 @@ public class DynamicSessionManager implements OwnCloudClientManager {
mSimpleFactoryManager.saveAllClients(context, accountType);
mSingleSessionManager.saveAllClients(context, accountType);
}
}
}

View File

@ -107,13 +107,11 @@ public class OwnCloudAccount {
* Method for deferred load of account attributes from AccountManager
*
* @param context
* @throws AccountNotFoundException
* @throws AuthenticatorException
* @throws IOException
* @throws OperationCanceledException
*/
public void loadCredentials(Context context) throws AuthenticatorException,
IOException, OperationCanceledException {
public void loadCredentials(Context context) throws AuthenticatorException, IOException, OperationCanceledException {
if (context == null) {
throw new IllegalArgumentException("Parameter 'context' cannot be null");
@ -151,5 +149,4 @@ public class OwnCloudAccount {
return null;
}
}
}

View File

@ -114,7 +114,7 @@ public class OwnCloudClient extends HttpClient {
status = method.execute();
checkFirstRedirection(method);
if (mFollowRedirects && !isIdPRedirection()) {
if (mFollowRedirects) {
status = followRedirection(method).getLastStatus();
}
@ -422,7 +422,7 @@ public class OwnCloudClient extends HttpClient {
*/
private boolean shouldInvalidateAccountCredentials(int httpStatusCode) {
boolean should = (httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED || isIdPRedirection()); // invalid credentials
boolean should = (httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED); // invalid credentials
should &= (mCredentials != null && // real credentials
!(mCredentials instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials));
@ -459,17 +459,6 @@ public class OwnCloudClient extends HttpClient {
mOwnCloudClientManager = clientManager;
}
/**
* Check if the redirection is to an identity provider such as SAML or wayf
*
* @return true if the redirection location includes SAML or wayf, false otherwise
*/
private boolean isIdPRedirection() {
return (mRedirectedLocation != null &&
(mRedirectedLocation.toUpperCase().contains("SAML") ||
mRedirectedLocation.toLowerCase().contains("wayf")));
}
public boolean followRedirects() {
return mFollowRedirects;
}

View File

@ -24,123 +24,13 @@
package com.owncloud.android.lib.common;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import com.owncloud.android.lib.common.accounts.AccountTypeUtils;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import java.io.IOException;
public class OwnCloudClientFactory {
final private static String TAG = OwnCloudClientFactory.class.getSimpleName();
/**
* Creates a OwnCloudClient setup for an ownCloud account
* <p>
* Do not call this method from the main thread.
*
* @param account The ownCloud account
* @param appContext Android application context
* @param currentActivity Caller {@link Activity}
* @return A OwnCloudClient object ready to be used
* @throws AuthenticatorException If the authenticator failed to get the authorization
* token for the account.
* @throws OperationCanceledException If the authenticator operation was cancelled while
* getting the authorization token for the account.
* @throws IOException If there was some I/O error while getting the
* authorization token for the account.
* @throws AccountNotFoundException If 'account' is unknown for the AccountManager
*/
public static OwnCloudClient createOwnCloudClient(Account account, Context appContext,
Activity currentActivity)
throws OperationCanceledException, AuthenticatorException, IOException,
AccountNotFoundException {
Uri baseUri = Uri.parse(AccountUtils.getBaseUrlForAccount(appContext, account));
AccountManager am = AccountManager.get(appContext);
// TODO avoid calling to getUserData here
boolean isOauth2 =
am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null;
boolean isSamlSso =
am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null;
OwnCloudClient client = createOwnCloudClient(baseUri, appContext, !isSamlSso);
String username = AccountUtils.getUsernameForAccount(account);
if (isOauth2) { // TODO avoid a call to getUserData here
AccountManagerFuture<Bundle> future = am.getAuthToken(
account,
AccountTypeUtils.getAuthTokenTypeAccessToken(account.type),
null,
currentActivity,
null,
null);
Bundle result = future.getResult();
String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
if (accessToken == null) {
throw new AuthenticatorException("WTF!");
}
client.setCredentials(
OwnCloudCredentialsFactory.newBearerCredentials(username, accessToken)
);
} else if (isSamlSso) { // TODO avoid a call to getUserData here
AccountManagerFuture<Bundle> future = am.getAuthToken(
account,
AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type),
null,
currentActivity,
null,
null);
Bundle result = future.getResult();
String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
if (accessToken == null) {
throw new AuthenticatorException("WTF!");
}
client.setCredentials(
OwnCloudCredentialsFactory.newSamlSsoCredentials(username, accessToken)
);
} else {
AccountManagerFuture<Bundle> future = am.getAuthToken(
account,
AccountTypeUtils.getAuthTokenTypePass(account.type),
null,
currentActivity,
null,
null
);
Bundle result = future.getResult();
String password = result.getString(AccountManager.KEY_AUTHTOKEN);
OwnCloudVersion version = AccountUtils.getServerVersionForAccount(account, appContext);
client.setCredentials(
OwnCloudCredentialsFactory.newBasicCredentials(
username,
password,
(version != null && version.isPreemptiveAuthenticationPreferred())
)
);
}
// Restore cookies
AccountUtils.restoreCookies(account, client, appContext);
return client;
}
/**
* Creates a OwnCloudClient to access a URL and sets the desired parameters for ownCloud
* client connections.

View File

@ -43,12 +43,10 @@ import java.io.IOException;
public interface OwnCloudClientManager {
OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws AccountNotFoundException,
OperationCanceledException, AuthenticatorException,
IOException;
OperationCanceledException, AuthenticatorException, IOException;
OwnCloudClient removeClientFor(OwnCloudAccount account);
void saveAllClients(Context context, String accountType)
throws AccountNotFoundException, AuthenticatorException,
IOException, OperationCanceledException;
void saveAllClients(Context context, String accountType) throws AccountNotFoundException, AuthenticatorException, IOException,
OperationCanceledException;
}

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
* Copyright (C) 2019 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -38,9 +38,6 @@ public class OwnCloudClientManagerFactory {
case ALWAYS_NEW_CLIENT:
return new SimpleFactoryManager();
case SINGLE_SESSION_PER_ACCOUNT:
return new SingleSessionManager();
case SINGLE_SESSION_PER_ACCOUNT_IF_SERVER_SUPPORTS_SERVER_MONITORING:
return new DynamicSessionManager();
@ -82,20 +79,11 @@ public class OwnCloudClientManagerFactory {
if (sDefaultSingleton == null) {
return false;
}
if (policy == Policy.ALWAYS_NEW_CLIENT &&
!(sDefaultSingleton instanceof SimpleFactoryManager)) {
return true;
}
if (policy == Policy.SINGLE_SESSION_PER_ACCOUNT &&
!(sDefaultSingleton instanceof SingleSessionManager)) {
return true;
}
return false;
return policy == Policy.ALWAYS_NEW_CLIENT && !(sDefaultSingleton instanceof SimpleFactoryManager);
}
public static enum Policy {
public enum Policy {
ALWAYS_NEW_CLIENT,
SINGLE_SESSION_PER_ACCOUNT,
SINGLE_SESSION_PER_ACCOUNT_IF_SERVER_SUPPORTS_SERVER_MONITORING
}
}
}

View File

@ -33,7 +33,7 @@ import android.net.Uri;
import android.util.Log;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.authentication.OwnCloudSamlSsoCredentials;
import com.owncloud.android.lib.common.http.HttpClient;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.IOException;
@ -56,11 +56,9 @@ public class SingleSessionManager implements OwnCloudClientManager {
private static final String TAG = SingleSessionManager.class.getSimpleName();
private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername =
new ConcurrentHashMap<>();
private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername = new ConcurrentHashMap<>();
private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername =
new ConcurrentHashMap<>();
private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername = new ConcurrentHashMap<>();
@Override
public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws OperationCanceledException,
@ -76,9 +74,7 @@ public class SingleSessionManager implements OwnCloudClientManager {
OwnCloudClient client = null;
String accountName = account.getName();
String sessionName = account.getCredentials() == null ? "" :
AccountUtils.buildAccountName(
account.getBaseUri(),
account.getCredentials().getAuthToken());
AccountUtils.buildAccountName(account.getBaseUri(), account.getCredentials().getAuthToken());
if (accountName != null) {
client = mClientsWithKnownUsername.get(accountName);
@ -113,16 +109,12 @@ public class SingleSessionManager implements OwnCloudClientManager {
context.getApplicationContext(),
true); // TODO remove dependency on OwnCloudClientFactory
client.setAccount(account);
client.setContext(context);
HttpClient.setContext(context);
client.setOwnCloudClientManager(this);
account.loadCredentials(context);
client.setCredentials(account.getCredentials());
if (client.getCredentials() instanceof OwnCloudSamlSsoCredentials) {
client.disableAutomaticCookiesHandling();
}
if (accountName != null) {
mClientsWithKnownUsername.put(accountName, client);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@ -198,10 +190,7 @@ public class SingleSessionManager implements OwnCloudClientManager {
while (accountNames.hasNext()) {
accountName = accountNames.next();
account = new Account(accountName, accountType);
AccountUtils.saveClient(
mClientsWithKnownUsername.get(accountName),
account,
context);
AccountUtils.saveClient(mClientsWithKnownUsername.get(accountName), account, context);
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
@ -218,7 +207,7 @@ public class SingleSessionManager implements OwnCloudClientManager {
if (am != null && account.getSavedAccount() != null) {
String recentCookies = am.getUserData(account.getSavedAccount(), AccountUtils.Constants.KEY_COOKIES);
String previousCookies = reusedClient.getCookiesString();
if (recentCookies != null && previousCookies != "" && !recentCookies.equals(previousCookies)) {
if (recentCookies != null && !previousCookies.equals("") && !recentCookies.equals(previousCookies)) {
AccountUtils.restoreCookies(account.getSavedAccount(), reusedClient, context);
}
}
@ -232,4 +221,4 @@ public class SingleSessionManager implements OwnCloudClientManager {
reusedClient.setBaseUri(recentUri);
}
}
}
}

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
* Copyright (C) 2019 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -41,9 +41,4 @@ public class AccountTypeUtils {
public static String getAuthTokenTypeRefreshToken(String accountType) {
return accountType + ".oauth2.refresh_token";
}
public static String getAuthTokenTypeSamlSessionCookie(String accountType) {
return accountType + ".saml.web_sso.session_cookie";
}
}

View File

@ -65,11 +65,7 @@ public class AccountUtils {
OwnCloudCredentials ownCloudCredentials = getCredentialsForAccount(context, account);
webDavUrlForAccount = getBaseUrlForAccount(context, account) + OwnCloudClient.WEBDAV_FILES_PATH_4_0
+ ownCloudCredentials.getUsername();
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
} catch (IOException e) {
} catch (OperationCanceledException | AuthenticatorException | IOException e) {
e.printStackTrace();
}
@ -148,11 +144,6 @@ public class AccountUtils {
String supportsOAuth2 = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_OAUTH2);
boolean isOauth2 = supportsOAuth2 != null && supportsOAuth2.equals("TRUE");
String supportsSamlSSo = am.getUserData(account,
AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO);
boolean isSamlSso = supportsSamlSSo != null && supportsSamlSSo.equals("TRUE");
String username = AccountUtils.getUsernameForAccount(account);
OwnCloudVersion version = new OwnCloudVersion(am.getUserData(account, Constants.KEY_OC_VERSION));
@ -164,14 +155,6 @@ public class AccountUtils {
credentials = OwnCloudCredentialsFactory.newBearerCredentials(username, accessToken);
} else if (isSamlSso) {
String accessToken = am.blockingGetAuthToken(
account,
AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type),
false);
credentials = OwnCloudCredentialsFactory.newSamlSsoCredentials(username, accessToken);
} else {
String password = am.blockingGetAuthToken(
account,
@ -317,10 +300,7 @@ public class AccountUtils {
* Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
*/
public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";
/**
* Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on.
*/
public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso";
/**
* OC account cookies
*/
@ -345,6 +325,5 @@ public class AccountUtils {
* OAuth2 refresh token
**/
public static final String KEY_OAUTH2_REFRESH_TOKEN = "oc_oauth2_refresh_token";
}
}
}

View File

@ -48,10 +48,6 @@ public class OwnCloudCredentialsFactory {
return new OwnCloudBearerCredentials(username, authToken);
}
public static OwnCloudCredentials newSamlSsoCredentials(String username, String sessionCookie) {
return new OwnCloudSamlSsoCredentials(username, sessionCookie);
}
public static final OwnCloudCredentials getAnonymousCredentials() {
if (sAnonymousCredentials == null) {
sAnonymousCredentials = new OwnCloudAnonymousCredentials();

View File

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

View File

@ -41,9 +41,6 @@ public class BearerCredentials {
* @param token The bearer token
*/
public BearerCredentials(String token) {
/*if (token == null) {
throw new IllegalArgumentException("Bearer token may not be null");
}*/
mAccessToken = (token == null) ? "" : token;
}

View File

@ -152,5 +152,4 @@ public class OwnCloudOAuth2RequestBuilder implements OAuth2RequestBuilder {
);
}
}
}

View File

@ -156,10 +156,13 @@ public class HttpClient {
* @param headerValue
*/
public static void addHeaderForAllRequests(String headerName, String headerValue) {
getOkHttpInterceptor()
.addRequestInterceptor(
new RequestHeaderInterceptor(headerName, headerValue)
);
HttpInterceptor httpInterceptor = getOkHttpInterceptor();
if(getOkHttpInterceptor() != null) {
httpInterceptor.addRequestInterceptor(
new RequestHeaderInterceptor(headerName, headerValue)
);
}
}
public static void deleteHeaderForAllRequests(String headerName) {

View File

@ -46,26 +46,32 @@ public class HttpInterceptor implements Interceptor {
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
for (RequestInterceptor interceptor : mRequestInterceptors) {
request = interceptor.intercept(request);
ListIterator<RequestInterceptor> requestInterceptorIterator = mRequestInterceptors.listIterator();
while (requestInterceptorIterator.hasNext()) {
RequestInterceptor currentRequestInterceptor = requestInterceptorIterator.next();
request = currentRequestInterceptor.intercept(request);
}
Response response = chain.proceed(request);
for (ResponseInterceptor interceptor : mResponseInterceptors) {
response = interceptor.intercept(response);
ListIterator<ResponseInterceptor> responseInterceptorIterator = mResponseInterceptors.listIterator();
while (responseInterceptorIterator.hasNext()) {
ResponseInterceptor currentResponseInterceptor = responseInterceptorIterator.next();
response = currentResponseInterceptor.intercept(response);
}
return response;
}
public HttpInterceptor addRequestInterceptor(RequestInterceptor requestInterceptor) {
mRequestInterceptors.add(requestInterceptor);
mRequestInterceptors.listIterator().add(requestInterceptor);
return this;
}
public HttpInterceptor addResponseInterceptor(ResponseInterceptor responseInterceptor) {
mResponseInterceptors.add(responseInterceptor);
mResponseInterceptors.listIterator().add(responseInterceptor);
return this;
}
@ -107,4 +113,4 @@ public class HttpInterceptor implements Interceptor {
public interface ResponseInterceptor {
Response intercept(Response response) throws IOException;
}
}
}

View File

@ -33,7 +33,6 @@ import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStoreException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
@ -46,20 +45,17 @@ public class AdvancedX509TrustManager implements X509TrustManager {
private static final String TAG = AdvancedX509TrustManager.class.getSimpleName();
private X509TrustManager mStandardTrustManager = null;
private X509TrustManager mStandardTrustManager;
private KeyStore mKnownServersKeyStore;
/**
* Constructor for AdvancedX509TrustManager
*
* @param knownServersKeyStore Local certificates store with server certificates explicitly trusted by the user.
* @throws CertStoreException When no default X509TrustManager instance was found in the system.
*/
public AdvancedX509TrustManager(KeyStore knownServersKeyStore)
throws NoSuchAlgorithmException, KeyStoreException, CertStoreException {
public AdvancedX509TrustManager(KeyStore knownServersKeyStore) throws NoSuchAlgorithmException, KeyStoreException {
super();
TrustManagerFactory factory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
factory.init((KeyStore) null);
mStandardTrustManager = findX509TrustManager(factory);
@ -71,13 +67,12 @@ public class AdvancedX509TrustManager implements X509TrustManager {
*
* @param factory TrustManagerFactory to inspect in the search for a X509TrustManager
* @return The first X509TrustManager found in factory.
* @throws CertStoreException When no X509TrustManager instance was found in factory
*/
private X509TrustManager findX509TrustManager(TrustManagerFactory factory) throws CertStoreException {
private X509TrustManager findX509TrustManager(TrustManagerFactory factory) {
TrustManager tms[] = factory.getTrustManagers();
for (int i = 0; i < tms.length; i++) {
if (tms[i] instanceof X509TrustManager) {
return (X509TrustManager) tms[i];
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager) tm;
}
}
return null;
@ -116,7 +111,7 @@ public class AdvancedX509TrustManager implements X509TrustManager {
previousCause = cause;
cause = cause.getCause();
}
if (cause != null && cause instanceof CertPathValidatorException) {
if (cause instanceof CertPathValidatorException) {
result.setCertPathValidatorException((CertPathValidatorException) cause);
} else {
result.setOtherCertificateException(c);

View File

@ -28,10 +28,10 @@ import java.util.Collection;
public interface ProgressiveDataTransferer {
public void addDatatransferProgressListener(OnDatatransferProgressListener listener);
void addDatatransferProgressListener(OnDatatransferProgressListener listener);
public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners);
void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners);
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener);
void removeDatatransferProgressListener(OnDatatransferProgressListener listener);
}

View File

@ -16,7 +16,7 @@ import okhttp3.OkHttpClient;
import java.io.IOException;
public abstract class RemoteOperation<T extends Object> implements Runnable {
public abstract class RemoteOperation<T> implements Runnable {
/**
* OCS API header name
@ -192,7 +192,7 @@ public abstract class RemoteOperation<T extends Object> implements Runnable {
/**
* Synchronously executes the remote operation
* <p>
*
* Do not call this method from the main thread.
*
* @param client Client object to reach an ownCloud server during the execution of

View File

@ -52,7 +52,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class RemoteOperationResult<T extends Object>
public class RemoteOperationResult<T>
implements Serializable {
/**
@ -70,6 +70,7 @@ public class RemoteOperationResult<T extends Object>
private ArrayList<String> mAuthenticate = new ArrayList<>();
private String mLastPermanentLocation = null;
private T mData = null;
/**
* Public constructor from result code.
* <p>
@ -253,10 +254,6 @@ public class RemoteOperationResult<T extends Object>
}
}
}
if (isIdPRedirection()) {
// overrides default ResultCode.UNKNOWN
mCode = ResultCode.UNAUTHORIZED;
}
}
/**
@ -491,12 +488,6 @@ public class RemoteOperationResult<T extends Object>
return mRedirectedLocation;
}
public boolean isIdPRedirection() {
return (mRedirectedLocation != null &&
(mRedirectedLocation.toUpperCase().contains("SAML") ||
mRedirectedLocation.toLowerCase().contains("wayf")));
}
/**
* Checks if is a non https connection
*
@ -583,4 +574,4 @@ public class RemoteOperationResult<T extends Object>
SPECIFIC_UNSUPPORTED_MEDIA_TYPE,
SPECIFIC_METHOD_NOT_ALLOWED
}
}
}

View File

@ -1,212 +1,87 @@
package com.owncloud.android.lib.common.utils;
import android.util.Log;
import timber.log.Timber;
import com.owncloud.android.lib.BuildConfig;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
public class Log_OC {
private static final String SIMPLE_DATE_FORMAT = "yyyy/MM/dd HH:mm:ss";
private static final String LOG_FOLDER_NAME = "log";
private static final long MAX_FILE_SIZE = 2000000; // 2MB
private static String mOwncloudDataFolderLog = "owncloud_log";
private static File mLogFile;
private static File mFolder;
private static BufferedWriter mBuf;
private static String[] mLogFileNames = {
"currentLog" + BuildConfig.BUILD_TYPE + ".txt",
"olderLog" + BuildConfig.BUILD_TYPE + ".txt"
};
private static boolean isMaxFileSizeReached = false;
private static boolean isEnabled = false;
private static String mOwncloudDataFolderLog;
public static void setLogDataFolder(String logFolder) {
mOwncloudDataFolderLog = logFolder;
}
public static void i(String TAG, String message) {
Log.i(TAG, message);
appendLog("I: " + TAG + " : " + message);
public static void i(String message) {
Timber.i(message);
}
public static void d(String message) {
Timber.d(message);
}
public static void d(String message, Exception e) {
Timber.d(e, message);
}
public static void e(String message) {
Timber.e(message);
}
public static void e(String message, Throwable e) {
Timber.e(e, message);
}
public static void v(String message) {
Timber.v(message);
}
public static void w(String message) {
Timber.w(message);
}
@Deprecated
public static void i(String tag, String message) {
Timber.i(message);
}
@Deprecated
public static void d(String TAG, String message) {
Log.d(TAG, message);
appendLog("D: " + TAG + " : " + message);
Timber.d(message);
}
@Deprecated
public static void d(String TAG, String message, Exception e) {
Log.d(TAG, message, e);
appendLog("D: " + TAG + " : " + message + " Exception : " + e.getStackTrace());
Timber.d(e, message);
}
@Deprecated
public static void e(String TAG, String message) {
Log.e(TAG, message);
appendLog("E: " + TAG + " : " + message);
Timber.e(message);
}
@Deprecated
public static void e(String TAG, String message, Throwable e) {
Log.e(TAG, message, e);
appendLog("E: " + TAG + " : " + message + " Exception : " + e.getStackTrace());
Timber.e(e, message);
}
@Deprecated
public static void v(String TAG, String message) {
Log.v(TAG, message);
appendLog("V: " + TAG + " : " + message);
Timber.v(message);
}
@Deprecated
public static void w(String TAG, String message) {
Log.w(TAG, message);
appendLog("W: " + TAG + " : " + message);
Timber.w(message);
}
/**
* Start doing logging
*
* @param storagePath : directory for keeping logs
*/
synchronized public static void startLogging(String storagePath) {
String logPath = storagePath + File.separator + mOwncloudDataFolderLog + File.separator + LOG_FOLDER_NAME;
mFolder = new File(logPath);
mLogFile = new File(mFolder + File.separator + mLogFileNames[0]);
boolean isFileCreated = false;
if (!mFolder.exists()) {
mFolder.mkdirs();
isFileCreated = true;
Log.d("LOG_OC", "Log file created");
}
try {
// Create the current log file if does not exist
mLogFile.createNewFile();
mBuf = new BufferedWriter(new FileWriter(mLogFile, true));
isEnabled = true;
if (isFileCreated) {
appendPhoneInfo();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (mBuf != null) {
try {
mBuf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void startLogging(String storagePath) {
LoggingHelper.INSTANCE.startLogging(
new File(storagePath+ File.separator + mOwncloudDataFolderLog), mOwncloudDataFolderLog);
}
synchronized public static void stopLogging() {
try {
if (mBuf != null) {
mBuf.close();
}
isEnabled = false;
mLogFile = null;
mFolder = null;
mBuf = null;
isMaxFileSizeReached = false;
} catch (IOException e) {
// Because we are stopping logging, we only log to Android console.
Log.e("OC_Log", "Closing log file failed: ", e);
} catch (Exception e) {
// This catch should never fire because we do null check on mBuf.
// But just for the sake of stability let's log this odd situation.
// Because we are stopping logging, we only log to Android console.
Log.e("OC_Log", "Stopping logging failed: ", e);
}
public static void stopLogging() {
LoggingHelper.INSTANCE.stopLogging();
}
/**
* Delete history logging
*/
public static void deleteHistoryLogging() {
File folderLogs = new File(mFolder + File.separator);
if (folderLogs.isDirectory()) {
String[] myFiles = folderLogs.list();
for (String fileName : myFiles) {
File fileInFolder = new File(folderLogs, fileName);
Log_OC.d("delete file", fileInFolder.getAbsoluteFile() + " " + fileInFolder.delete());
}
}
}
/**
* Append the info of the device
*/
private static void appendPhoneInfo() {
appendLog("Model : " + android.os.Build.MODEL);
appendLog("Brand : " + android.os.Build.BRAND);
appendLog("Product : " + android.os.Build.PRODUCT);
appendLog("Device : " + android.os.Build.DEVICE);
appendLog("Version-Codename : " + android.os.Build.VERSION.CODENAME);
appendLog("Version-Release : " + android.os.Build.VERSION.RELEASE);
}
/**
* Append to the log file the info passed
*
* @param text : text for adding to the log file
*/
synchronized private static void appendLog(String text) {
if (isEnabled) {
if (isMaxFileSizeReached) {
// Move current log file info to another file (old logs)
File olderFile = new File(mFolder + File.separator + mLogFileNames[1]);
if (mLogFile.exists()) {
mLogFile.renameTo(olderFile);
}
// Construct a new file for current log info
mLogFile = new File(mFolder + File.separator + mLogFileNames[0]);
isMaxFileSizeReached = false;
}
String timeStamp = new SimpleDateFormat(SIMPLE_DATE_FORMAT, Locale.ENGLISH).format(Calendar.getInstance().getTime());
try {
mBuf = new BufferedWriter(new FileWriter(mLogFile, true));
mBuf.newLine();
mBuf.write(timeStamp + " " + text);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
mBuf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// Check if current log file size is bigger than the max file size defined
if (mLogFile.length() > MAX_FILE_SIZE) {
isMaxFileSizeReached = true;
}
}
}
public static String[] getLogFileNames() {
return mLogFileNames;
}
}

View File

@ -0,0 +1,24 @@
package com.owncloud.android.lib.common.utils
import info.hannes.timber.FileLoggingTree
import info.hannes.timber.fileLoggingTree
import timber.log.Timber
import java.io.File
object LoggingHelper {
fun startLogging(directory: File, storagePath: String) {
fileLoggingTree()?.let {
Timber.forest().drop(Timber.forest().indexOf(it))
}
if (!directory.exists())
directory.mkdirs()
Timber.plant(FileLoggingTree(directory, filename = storagePath, delegator = Log_OC::class.java))
}
fun stopLogging() {
fileLoggingTree()?.let {
Timber.forest().drop(Timber.forest().indexOf(it))
}
}
}

View File

@ -0,0 +1,31 @@
/**
* ownCloud Android client application
*
* @author David González Verdugo
*
* Copyright (C) 2019 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.lib.resources
import com.owncloud.android.lib.common.OwnCloudClient
/**
* Facade to perform network calls without the verbosity of remote operations
*/
interface Service {
val client: OwnCloudClient
}

View File

@ -78,8 +78,9 @@ public class FileUtils {
(path.contains("\\") || path.contains("<") || path.contains(">") ||
path.contains(":") || path.contains("\"") || path.contains("|") ||
path.contains("?") || path.contains("*"))) {
result = false;
}
return result;
}
}
}

View File

@ -33,16 +33,12 @@ import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK
import com.owncloud.android.lib.common.utils.Log_OC
import org.json.JSONObject
import java.net.URL
import java.util.ArrayList
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK
import com.owncloud.android.lib.testing.OpenForTesting
/**
* Created by masensio on 08/10/2015.
*
@ -73,7 +69,6 @@ import com.owncloud.android.lib.testing.OpenForTesting
* @author David A. Velasco
* @author David González Verdugo
*/
@OpenForTesting
class GetRemoteShareesOperation
/**
* Constructor

View File

@ -75,7 +75,7 @@ class GetRemoteSharesForFileOperation(
val getMethod = GetMethod(URL(uriBuilder.build().toString()))
getMethod.addRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE)
getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
val status = client.executeHttpMethod(getMethod)

View File

@ -1,91 +0,0 @@
/* ownCloud Android Library is available under MIT license
*
* Copyright (C) 2019 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.shares;
import android.net.Uri;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.net.URL;
/**
* Get the data from the server about ALL the known shares owned by the requester.
*
* @author masensio
* @author David A. Velasco
* @author David González Verdugo
*/
public class GetRemoteSharesOperation extends RemoteOperation {
private static final String TAG = GetRemoteSharesOperation.class.getSimpleName();
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result;
try {
Uri requestUri = client.getBaseUri();
Uri.Builder uriBuilder = requestUri.buildUpon();
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH);
GetMethod getMethod = new GetMethod(
new URL(client.getBaseUri() + ShareUtils.SHARING_API_PATH)
);
getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE);
int status = client.executeHttpMethod(getMethod);
if (isSuccess(status)) {
// Parse xml response and obtain the list of shares
ShareToRemoteOperationResultParser parser = new ShareToRemoteOperationResultParser(
new ShareXMLParser()
);
parser.setOwnCloudVersion(client.getOwnCloudVersion());
parser.setServerBaseUri(client.getBaseUri());
result = parser.parse(getMethod.getResponseBodyAsString());
} else {
result = new RemoteOperationResult<>(getMethod);
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Exception while getting remote shares ", e);
}
return result;
}
private boolean isSuccess(int status) {
return (status == HttpConstants.HTTP_OK);
}
}

View File

@ -24,11 +24,7 @@
package com.owncloud.android.lib.resources.shares
import android.os.Parcel
import android.os.Parcelable
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.lib.resources.files.FileUtils
import java.io.Serializable
/**
* Contains the data of a Share from the Share API
@ -37,124 +33,27 @@ import java.io.Serializable
* @author David A. Velasco
* @author David González Verdugo
*/
class RemoteShare : Parcelable, Serializable {
var id: Long = 0
var shareWith: String = ""
var path: String = ""
var token: String = ""
var sharedWithDisplayName: String = ""
var sharedWithAdditionalInfo: String = ""
var name: String = ""
var shareLink: String = ""
var fileSource: Long = 0
var itemSource: Long = 0
var shareType: ShareType? = null
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 userId: Long = 0
data class RemoteShare(
var id: Long = 0,
var shareWith: String = "",
var path: String = "",
var token: String = "",
var sharedWithDisplayName: String = "",
var sharedWithAdditionalInfo: String = "",
var name: String = "",
var shareLink: String = "",
var fileSource: Long = 0,
var itemSource: Long = 0,
var shareType: ShareType? = ShareType.UNKNOWN,
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 userId: Long = 0,
val isValid: Boolean = id > -1
constructor() : super() {
resetData()
}
constructor(path: String?) {
resetData()
if (path.isNullOrEmpty() || !path.startsWith(FileUtils.PATH_SEPARATOR)) {
Log_OC.e(TAG, "Trying to create a RemoteShare with a non valid path")
throw IllegalArgumentException("Trying to create a RemoteShare with a non valid path: " + path!!)
}
this.path = path
}
/**
* Used internally. Reset all file properties
*/
private fun resetData() {
id = -1
shareWith = ""
path = ""
token = ""
sharedWithDisplayName = ""
sharedWithAdditionalInfo = ""
name = ""
shareLink = ""
fileSource = 0
itemSource = 0
shareType = ShareType.NO_SHARED
permissions = DEFAULT_PERMISSION
sharedDate = INIT_SHARED_DATE
expirationDate = INIT_EXPIRATION_DATE_IN_MILLIS
sharedWithAdditionalInfo = ""
isFolder = false
userId = -1
}
/**
* Reconstruct from parcel
*
* @param source The source parcel
*/
protected constructor(source: Parcel) {
readFromParcel(source)
}
fun readFromParcel(source: Parcel) {
id = source.readLong()
shareWith = source.readString().toString()
path = source.readString().toString()
token = source.readString().toString()
sharedWithDisplayName = source.readString().toString()
sharedWithAdditionalInfo = source.readString().toString()
name = source.readString().toString()
shareLink = source.readString().toString()
fileSource = source.readLong()
itemSource = source.readLong()
shareType = ShareType.NO_SHARED
try {
shareType = source.readString()?.let { ShareType.valueOf(it) }
} catch (x: IllegalArgumentException) {
}
permissions = source.readInt()
sharedDate = source.readLong()
expirationDate = source.readLong()
isFolder = source.readInt() == 0
userId = source.readLong()
}
override fun describeContents(): Int = this.hashCode()
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeLong(id)
dest.writeString(shareWith)
dest.writeString(path)
dest.writeString(token)
dest.writeString(sharedWithDisplayName)
dest.writeString(sharedWithAdditionalInfo)
dest.writeString(name)
dest.writeString(shareLink)
dest.writeLong(fileSource)
dest.writeLong(itemSource)
dest.writeString(shareType?.name ?: "")
dest.writeInt(permissions)
dest.writeLong(sharedDate)
dest.writeLong(expirationDate)
dest.writeInt(if (isFolder) 1 else 0)
dest.writeLong(userId)
}
) {
companion object {
/**
* Generated - should be refreshed every time the class changes!!
*/
private const val serialVersionUID = 4124975224281327921L
private val TAG = RemoteShare::class.java.simpleName
const val DEFAULT_PERMISSION = -1
const val READ_PERMISSION_FLAG = 1
const val UPDATE_PERMISSION_FLAG = 2
@ -180,18 +79,44 @@ class RemoteShare : Parcelable, Serializable {
const val INIT_EXPIRATION_DATE_IN_MILLIS: Long = 0
const val INIT_SHARED_DATE: Long = 0
}
}
/**
* Parcelable Methods
*/
@JvmField
val CREATOR: Parcelable.Creator<RemoteShare> = object : Parcelable.Creator<RemoteShare> {
override fun createFromParcel(source: Parcel): RemoteShare {
return RemoteShare(source)
}
/**
* // TODO This type is already included in the domain but we still need it here since the parsing takes place in this library for the moment
*
* Enum for Share Type, with values:
* -1 - Unknown
* 0 - Shared by user
* 1 - Shared by group
* 3 - Shared by public link
* 4 - Shared by e-mail
* 5 - Shared by contact
* 6 - Federated
*
* @author masensio
*/
override fun newArray(size: Int): Array<RemoteShare?> {
return arrayOfNulls(size)
enum class ShareType constructor(val value: Int) {
UNKNOWN(-1),
USER(0),
GROUP(1),
PUBLIC_LINK(3),
EMAIL(4),
CONTACT(5),
FEDERATED(6);
companion object {
fun fromValue(value: Int): ShareType? {
return when (value) {
-1 -> UNKNOWN
0 -> USER
1 -> GROUP
3 -> PUBLIC_LINK
4 -> EMAIL
5 -> CONTACT
6 -> FEDERATED
else -> null
}
}
}

View File

@ -0,0 +1,54 @@
/**
* ownCloud Android client application
*
* @author David González Verdugo
*
* Copyright (C) 2019 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.lib.resources.shares
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.Service
interface ShareService : Service {
fun getShares(
remoteFilePath: String,
reshares: Boolean,
subfiles: Boolean
): RemoteOperationResult<ShareParserResult>
fun insertShare(
remoteFilePath: String,
shareType: ShareType,
shareWith: String,
permissions: Int,
name: String,
password: String,
expirationDate: Long,
publicUpload: Boolean
): RemoteOperationResult<ShareParserResult>
fun updateShare(
remoteId: Long,
name: String,
password: String?,
expirationDate: Long,
permissions: Int,
publicUpload: Boolean
): RemoteOperationResult<ShareParserResult>
fun deleteShare(remoteId: Long): RemoteOperationResult<ShareParserResult>
}

View File

@ -1,64 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2019 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.shares
/**
* Enum for Share Type, with values:
* -1 - No shared
* 0 - Shared by user
* 1 - Shared by group
* 3 - Shared by public link
* 4 - Shared by e-mail
* 5 - Shared by contact
* 6 - Federated
*
* @author masensio
*/
enum class ShareType constructor(val value: Int) {
NO_SHARED(-1),
USER(0),
GROUP(1),
PUBLIC_LINK(3),
EMAIL(4),
CONTACT(5),
FEDERATED(6);
companion object {
fun fromValue(value: Int): ShareType? {
return when (value) {
-1 -> NO_SHARED
0 -> USER
1 -> GROUP
3 -> PUBLIC_LINK
4 -> EMAIL
5 -> CONTACT
6 -> FEDERATED
else -> null
}
}
}
}

View File

@ -0,0 +1,34 @@
/**
* ownCloud Android client application
*
* @author David González Verdugo
*
* Copyright (C) 2019 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.lib.resources.shares
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.Service
import org.json.JSONObject
import java.util.ArrayList
interface ShareeService : Service {
fun getSharees(
searchString: String,
page: Int,
perPage: Int
): RemoteOperationResult<ArrayList<JSONObject>>
}

View File

@ -47,7 +47,6 @@ import java.util.Locale
* @author David A. Velasco
* @author David González Verdugo
*/
class UpdateRemoteShareOperation
/**
* Constructor. No update is initialized by default, need to be applied with setters below.

View File

@ -1,65 +0,0 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2019 ownCloud GmbH.
* @author masensio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.status
/**
* Enum for Boolean Type in RemoteCapability parameters, with values:
* -1 - Unknown
* 0 - False
* 1 - True
*/
enum class CapabilityBooleanType private constructor(val value: Int) {
UNKNOWN(-1),
FALSE(0),
TRUE(1);
val isUnknown: Boolean
get() = value == -1
val isFalse: Boolean
get() = value == 0
val isTrue: Boolean
get() = value == 1
companion object {
fun fromValue(value: Int): CapabilityBooleanType? {
return when (value) {
-1 -> UNKNOWN
0 -> FALSE
1 -> TRUE
else -> null
}
}
fun fromBooleanValue(boolValue: Boolean): CapabilityBooleanType {
return if (boolValue) {
TRUE
} else {
FALSE
}
}
}
}

View File

@ -0,0 +1,28 @@
/**
* ownCloud Android client application
*
* @author David González Verdugo
*
* Copyright (C) 2019 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.lib.resources.status
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.Service
interface CapabilityService: Service {
fun getCapabilities() : RemoteOperationResult<RemoteCapability>
}

View File

@ -36,6 +36,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCo
import com.owncloud.android.lib.common.utils.Log_OC
import org.json.JSONObject
import java.net.URL
import com.owncloud.android.lib.resources.status.RemoteCapability.CapabilityBooleanType
/**
* Get the Capabilities from the server
@ -60,7 +61,7 @@ class GetRemoteCapabilitiesOperation : RemoteOperation<RemoteCapability>() {
val getMethod = GetMethod(URL(uriBuilder.build().toString()))
getMethod.addRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE)
getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
val status = client.executeHttpMethod(getMethod)
@ -123,6 +124,10 @@ class GetRemoteCapabilitiesOperation : RemoteOperation<RemoteCapability>() {
respFilesSharing.getBoolean(PROPERTY_API_ENABLED)
)
}
if (respFilesSharing.has(PROPERTY_SEARCH_MIN_LENGTH)){
capability.filesSharingSearchMinLength = respFilesSharing.getInt(
PROPERTY_SEARCH_MIN_LENGTH)
}
if (respFilesSharing.has(NODE_PUBLIC)) {
val respPublic = respFilesSharing.getJSONObject(NODE_PUBLIC)
@ -263,64 +268,65 @@ class GetRemoteCapabilitiesOperation : RemoteOperation<RemoteCapability>() {
private val TAG = GetRemoteCapabilitiesOperation::class.java.simpleName
// OCS Routes
private val OCS_ROUTE = "ocs/v2.php/cloud/capabilities"
private const val OCS_ROUTE = "ocs/v2.php/cloud/capabilities"
// Arguments - names
private val PARAM_FORMAT = "format"
private const val PARAM_FORMAT = "format"
// Arguments - constant values
private val VALUE_FORMAT = "json"
private const val VALUE_FORMAT = "json"
// JSON Node names
private val NODE_OCS = "ocs"
private const val NODE_OCS = "ocs"
private val NODE_META = "meta"
private const val NODE_META = "meta"
private val NODE_DATA = "data"
private val NODE_VERSION = "version"
private const val NODE_DATA = "data"
private const val NODE_VERSION = "version"
private val NODE_CAPABILITIES = "capabilities"
private val NODE_CORE = "core"
private const val NODE_CAPABILITIES = "capabilities"
private const val NODE_CORE = "core"
private val NODE_FILES_SHARING = "files_sharing"
private val NODE_PUBLIC = "public"
private val NODE_PASSWORD = "password"
private val NODE_ENFORCED_FOR = "enforced_for"
private val NODE_EXPIRE_DATE = "expire_date"
private val NODE_USER = "user"
private val NODE_FEDERATION = "federation"
private val NODE_FILES = "files"
private const val NODE_FILES_SHARING = "files_sharing"
private const val NODE_PUBLIC = "public"
private const val NODE_PASSWORD = "password"
private const val NODE_ENFORCED_FOR = "enforced_for"
private const val NODE_EXPIRE_DATE = "expire_date"
private const val NODE_USER = "user"
private const val NODE_FEDERATION = "federation"
private const val NODE_FILES = "files"
private val PROPERTY_STATUS = "status"
private val PROPERTY_STATUS_OK = "ok"
private val PROPERTY_STATUSCODE = "statuscode"
private val PROPERTY_MESSAGE = "message"
private const val PROPERTY_STATUS = "status"
private const val PROPERTY_STATUS_OK = "ok"
private const val PROPERTY_STATUSCODE = "statuscode"
private const val PROPERTY_MESSAGE = "message"
private val PROPERTY_POLLINTERVAL = "pollinterval"
private const val PROPERTY_POLLINTERVAL = "pollinterval"
private val PROPERTY_MAJOR = "major"
private val PROPERTY_MINOR = "minor"
private val PROPERTY_MICRO = "micro"
private val PROPERTY_STRING = "string"
private val PROPERTY_EDITION = "edition"
private const val PROPERTY_MAJOR = "major"
private const val PROPERTY_MINOR = "minor"
private const val PROPERTY_MICRO = "micro"
private const val PROPERTY_STRING = "string"
private const val PROPERTY_EDITION = "edition"
private val PROPERTY_API_ENABLED = "api_enabled"
private val PROPERTY_ENABLED = "enabled"
private val PROPERTY_ENFORCED = "enforced"
private val PROPERTY_ENFORCED_READ_ONLY = "read_only"
private val PROPERTY_ENFORCED_READ_WRITE = "read_write"
private val PROPERTY_ENFORCED_UPLOAD_ONLY = "upload_only"
private val PROPERTY_DAYS = "days"
private val PROPERTY_SEND_MAIL = "send_mail"
private val PROPERTY_UPLOAD = "upload"
private val PROPERTY_UPLOAD_ONLY = "supports_upload_only"
private val PROPERTY_MULTIPLE = "multiple"
private val PROPERTY_RESHARING = "resharing"
private val PROPERTY_OUTGOING = "outgoing"
private val PROPERTY_INCOMING = "incoming"
private const val PROPERTY_API_ENABLED = "api_enabled"
private const val PROPERTY_ENABLED = "enabled"
private const val PROPERTY_ENFORCED = "enforced"
private const val PROPERTY_ENFORCED_READ_ONLY = "read_only"
private const val PROPERTY_ENFORCED_READ_WRITE = "read_write"
private const val PROPERTY_ENFORCED_UPLOAD_ONLY = "upload_only"
private const val PROPERTY_DAYS = "days"
private const val PROPERTY_SEARCH_MIN_LENGTH = "search_min_length"
private const val PROPERTY_SEND_MAIL = "send_mail"
private const val PROPERTY_UPLOAD = "upload"
private const val PROPERTY_UPLOAD_ONLY = "supports_upload_only"
private const val PROPERTY_MULTIPLE = "multiple"
private const val PROPERTY_RESHARING = "resharing"
private const val PROPERTY_OUTGOING = "outgoing"
private const val PROPERTY_INCOMING = "incoming"
private val PROPERTY_BIGFILECHUNKING = "bigfilechunking"
private val PROPERTY_UNDELETE = "undelete"
private val PROPERTY_VERSIONING = "versioning"
private const val PROPERTY_BIGFILECHUNKING = "bigfilechunking"
private const val PROPERTY_UNDELETE = "undelete"
private const val PROPERTY_VERSIONING = "versioning"
}
}

View File

@ -27,9 +27,7 @@ package com.owncloud.android.lib.resources.status
import android.os.Parcel
import android.os.Parcelable
import com.owncloud.android.lib.testing.OpenForTesting
@OpenForTesting
class OwnCloudVersion(version: String) : Comparable<OwnCloudVersion>, Parcelable {
// format is in version

View File

@ -1,6 +1,7 @@
/* ownCloud Android Library is available under MIT license
* @author masensio
* @author David González Verdugo
* @author Abel García de Prada
* Copyright (C) 2019 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
@ -28,74 +29,76 @@ package com.owncloud.android.lib.resources.status
/**
* Contains data of the Capabilities for an account, from the Capabilities API
*/
class RemoteCapability {
var accountName: String
data class RemoteCapability(
var accountName: String = "",
// Server version
var versionMayor: Int
var versionMinor: Int
var versionMicro: Int
var versionString: String
var versionEdition: String
var versionMayor: Int = 0,
var versionMinor: Int = 0,
var versionMicro: Int = 0,
var versionString: String = "",
var versionEdition: String = "",
// Core PollInterval
var corePollinterval: Int
var corePollinterval: Int = 0,
// Files Sharing
var filesSharingApiEnabled: CapabilityBooleanType
var filesSharingPublicEnabled: CapabilityBooleanType
var filesSharingPublicPasswordEnforced: CapabilityBooleanType
var filesSharingPublicPasswordEnforcedReadOnly: CapabilityBooleanType
var filesSharingPublicPasswordEnforcedReadWrite: CapabilityBooleanType
var filesSharingPublicPasswordEnforcedUploadOnly: CapabilityBooleanType
var filesSharingPublicExpireDateEnabled: CapabilityBooleanType
var filesSharingPublicExpireDateDays: Int
var filesSharingPublicExpireDateEnforced: CapabilityBooleanType
var filesSharingPublicSendMail: CapabilityBooleanType
var filesSharingPublicUpload: CapabilityBooleanType
var filesSharingPublicMultiple: CapabilityBooleanType
var filesSharingPublicSupportsUploadOnly: CapabilityBooleanType
var filesSharingUserSendMail: CapabilityBooleanType
var filesSharingResharing: CapabilityBooleanType
var filesSharingFederationOutgoing: CapabilityBooleanType
var filesSharingFederationIncoming: CapabilityBooleanType
var filesSharingApiEnabled: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingSearchMinLength: Int? = DEFAULT_MIN_CHARACTERS_TO_SEARCH,
var filesSharingPublicEnabled: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingPublicPasswordEnforced: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingPublicPasswordEnforcedReadOnly: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingPublicPasswordEnforcedReadWrite: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingPublicPasswordEnforcedUploadOnly: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingPublicExpireDateEnabled: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingPublicExpireDateDays: Int = 0,
var filesSharingPublicExpireDateEnforced: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingPublicSendMail: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingPublicUpload: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingPublicMultiple: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingPublicSupportsUploadOnly: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingUserSendMail: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingResharing: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingFederationOutgoing: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesSharingFederationIncoming: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
// Files
var filesBigFileChunking: CapabilityBooleanType
var filesUndelete: CapabilityBooleanType
var filesVersioning: CapabilityBooleanType
var filesBigFileChunking: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesUndelete: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN,
var filesVersioning: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN
) {
/**
* Enum for Boolean Type in capabilities, with values:
* -1 - Unknown
* 0 - False
* 1 - True
*/
enum class CapabilityBooleanType constructor(val value: Int) {
UNKNOWN(-1),
FALSE(0),
TRUE(1);
init {
accountName = ""
companion object {
fun fromValue(value: Int): CapabilityBooleanType? {
return when (value) {
-1 -> UNKNOWN
0 -> FALSE
1 -> TRUE
else -> null
}
}
versionMayor = 0
versionMinor = 0
versionMicro = 0
versionString = ""
versionEdition = ""
fun fromBooleanValue(boolValue: Boolean): CapabilityBooleanType {
return if (boolValue) {
TRUE
} else {
FALSE
}
}
}
}
corePollinterval = 0
filesSharingApiEnabled = CapabilityBooleanType.UNKNOWN
filesSharingPublicEnabled = CapabilityBooleanType.UNKNOWN
filesSharingPublicPasswordEnforced = CapabilityBooleanType.UNKNOWN
filesSharingPublicPasswordEnforcedReadOnly = CapabilityBooleanType.UNKNOWN
filesSharingPublicPasswordEnforcedReadWrite = CapabilityBooleanType.UNKNOWN
filesSharingPublicPasswordEnforcedUploadOnly = CapabilityBooleanType.UNKNOWN
filesSharingPublicExpireDateEnabled = CapabilityBooleanType.UNKNOWN
filesSharingPublicExpireDateDays = 0
filesSharingPublicExpireDateEnforced = CapabilityBooleanType.UNKNOWN
filesSharingPublicSendMail = CapabilityBooleanType.UNKNOWN
filesSharingPublicUpload = CapabilityBooleanType.UNKNOWN
filesSharingPublicMultiple = CapabilityBooleanType.UNKNOWN
filesSharingPublicSupportsUploadOnly = CapabilityBooleanType.UNKNOWN
filesSharingUserSendMail = CapabilityBooleanType.UNKNOWN
filesSharingResharing = CapabilityBooleanType.UNKNOWN
filesSharingFederationOutgoing = CapabilityBooleanType.UNKNOWN
filesSharingFederationIncoming = CapabilityBooleanType.UNKNOWN
filesBigFileChunking = CapabilityBooleanType.UNKNOWN
filesUndelete = CapabilityBooleanType.UNKNOWN
filesVersioning = CapabilityBooleanType.UNKNOWN
companion object {
private const val DEFAULT_MIN_CHARACTERS_TO_SEARCH = 2
}
}

View File

@ -58,15 +58,13 @@ public class GetRemoteUserAvatarOperation extends RemoteOperation<GetRemoteUserA
*/
private int mDimension;
/**
* Etag of current local copy of the avatar; if not null, remote avatar will be downloaded only
* if its Etag changed.
*/
private String mCurrentEtag;
@Deprecated
public GetRemoteUserAvatarOperation(int dimension, String currentEtag) {
this(dimension);
}
public GetRemoteUserAvatarOperation(int dimension) {
mDimension = dimension;
mCurrentEtag = currentEtag;
}
@Override
@ -78,9 +76,7 @@ public class GetRemoteUserAvatarOperation extends RemoteOperation<GetRemoteUserA
ByteArrayOutputStream bos = null;
try {
final String url =
client.getBaseUri() + NON_OFFICIAL_AVATAR_PATH +
client.getCredentials().getUsername() + "/" + mDimension;
final String url = client.getBaseUri() + NON_OFFICIAL_AVATAR_PATH + client.getCredentials().getUsername() + "/" + mDimension;
Log_OC.d(TAG, "avatar URI: " + url);
getMethod = new GetMethod(new URL(url));
@ -101,9 +97,7 @@ public class GetRemoteUserAvatarOperation extends RemoteOperation<GetRemoteUserA
String contentType = getMethod.getResponseHeader(HttpConstants.CONTENT_TYPE_HEADER);
if (contentType == null || !contentType.startsWith("image")) {
Log_OC.e(
TAG, "Not an image, failing with no avatar"
);
Log_OC.e(TAG, "Not an image, failing with no avatar");
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.FILE_NOT_FOUND);
return result;
}
@ -115,12 +109,10 @@ public class GetRemoteUserAvatarOperation extends RemoteOperation<GetRemoteUserA
bis = new BufferedInputStream(inputStream);
bos = new ByteArrayOutputStream(totalToTransfer);
long transferred = 0;
byte[] bytes = new byte[4096];
int readResult = 0;
int readResult;
while ((readResult = bis.read(bytes)) != -1) {
bos.write(bytes, 0, readResult);
transferred += readResult;
}
// TODO check total bytes transferred?

View File

@ -1,31 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.owncloud.android.lib.testing
/**
* This annotation allows us to open some classes for mocking purposes while they are final in
* release builds.
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class OpenClass
/**
* Annotate a class with [OpenForTesting] if you want it to be extendable in debug builds.
*/
@OpenClass
@Target(AnnotationTarget.CLASS)
annotation class OpenForTesting