1
0
mirror of https://github.com/owncloud/android-library.git synced 2026-08-11 08:22:56 +00:00

Rename library module from lib to owncloudComLibrary

This commit is contained in:
davigonz
2018-12-19 16:58:08 +01:00
parent 8a8906154c
commit badc15b741
107 changed files with 2 additions and 2 deletions
+26
View File
@@ -0,0 +1,26 @@
apply plugin: 'com.android.library'
dependencies {
api 'com.squareup.okhttp3:okhttp:3.12.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.11"
implementation 'com.gitlab.ownclouders:dav4android:oc_support'
}
android {
compileSdkVersion 26
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
}
lintOptions {
abortOnError false
ignoreWarnings true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ownCloud Android Library is available under MIT license
Copyright (C) 2016 ownCloud GmbH.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.owncloud.android.lib"
android:versionCode="1"
android:versionName="1.0" >
<!-- USE_CREDENTIALS, MANAGE_ACCOUNTS and AUTHENTICATE_ACCOUNTS are needed for API < 23.
In API >= 23 the do not exist anymore -->
<uses-permission
android:name="android.permission.MANAGE_ACCOUNTS"
android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
@@ -0,0 +1,63 @@
package com.owncloud.android.lib.common;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import java.io.IOException;
/**
* Dynamic implementation of {@link OwnCloudClientManager}.
*
* 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}
*
* @author David A. Velasco
*/
public class DynamicSessionManager implements OwnCloudClientManager {
private SimpleFactoryManager mSimpleFactoryManager = new SimpleFactoryManager();
private SingleSessionManager mSingleSessionManager = new SingleSessionManager();
@Override
public OwnCloudClient getClientFor(OwnCloudAccount account, Context context)
throws AccountUtils.AccountNotFoundException,
OperationCanceledException, AuthenticatorException, IOException {
OwnCloudVersion ownCloudVersion = null;
if (account.getSavedAccount() != null) {
ownCloudVersion = AccountUtils.getServerVersionForAccount(
account.getSavedAccount(), context
);
}
if (ownCloudVersion != null && ownCloudVersion.isSessionMonitoringSupported()) {
return mSingleSessionManager.getClientFor(account, context);
} else {
return mSimpleFactoryManager.getClientFor(account, context);
}
}
@Override
public OwnCloudClient removeClientFor(OwnCloudAccount account) {
OwnCloudClient clientRemovedFromFactoryManager = mSimpleFactoryManager.removeClientFor(account);
OwnCloudClient clientRemovedFromSingleSessionManager = mSingleSessionManager.removeClientFor(account);
if (clientRemovedFromSingleSessionManager != null) {
return clientRemovedFromSingleSessionManager;
} else {
return clientRemovedFromFactoryManager;
}
// clientRemoved and clientRemoved2 should not be != null at the same time
}
@Override
public void saveAllClients(Context context, String accountType) {
mSimpleFactoryManager.saveAllClients(context, accountType);
mSingleSessionManager.saveAllClients(context, accountType);
}
}
@@ -0,0 +1,159 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common;
import java.io.IOException;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.net.Uri;
/**
* OwnCloud Account
*
* @author David A. Velasco
*/
public class OwnCloudAccount {
private Uri mBaseUri;
private OwnCloudCredentials mCredentials;
private String mDisplayName;
private String mSavedAccountName;
private Account mSavedAccount;
/**
* Constructor for already saved OC accounts.
*
* Do not use for anonymous credentials.
*/
public OwnCloudAccount(Account savedAccount, Context context) throws AccountNotFoundException {
if (savedAccount == null) {
throw new IllegalArgumentException("Parameter 'savedAccount' cannot be null");
}
if (context == null) {
throw new IllegalArgumentException("Parameter 'context' cannot be null");
}
mSavedAccount = savedAccount;
mSavedAccountName = savedAccount.name;
mCredentials = null; // load of credentials is delayed
AccountManager ama = AccountManager.get(context.getApplicationContext());
String baseUrl = ama.getUserData(mSavedAccount, AccountUtils.Constants.KEY_OC_BASE_URL);
if (baseUrl == null ) {
throw new AccountNotFoundException(mSavedAccount, "Account not found", null);
}
mBaseUri = Uri.parse(AccountUtils.getBaseUrlForAccount(context, mSavedAccount));
mDisplayName = ama.getUserData(mSavedAccount, AccountUtils.Constants.KEY_DISPLAY_NAME);
}
/**
* Constructor for non yet saved OC accounts.
*
* @param baseUri URI to the OC server to get access to.
* @param credentials Credentials to authenticate in the server. NULL is valid for anonymous credentials.
*/
public OwnCloudAccount(Uri baseUri, OwnCloudCredentials credentials) {
if (baseUri == null) {
throw new IllegalArgumentException("Parameter 'baseUri' cannot be null");
}
mSavedAccount = null;
mSavedAccountName = null;
mBaseUri = baseUri;
mCredentials = credentials != null ?
credentials : OwnCloudCredentialsFactory.getAnonymousCredentials();
String username = mCredentials.getUsername();
if (username != null) {
mSavedAccountName = AccountUtils.buildAccountName(mBaseUri, username);
}
}
/**
* 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 {
if (context == null) {
throw new IllegalArgumentException("Parameter 'context' cannot be null");
}
if (mSavedAccount != null) {
mCredentials = AccountUtils.getCredentialsForAccount(context, mSavedAccount);
}
}
public Uri getBaseUri() {
return mBaseUri;
}
public OwnCloudCredentials getCredentials() {
return mCredentials;
}
public String getName() {
return mSavedAccountName;
}
public Account getSavedAccount() {
return mSavedAccount;
}
public String getDisplayName() {
if (mDisplayName != null && mDisplayName.length() > 0) {
return mDisplayName;
} else if (mCredentials != null) {
return mCredentials.getUsername();
} else if (mSavedAccount != null) {
return AccountUtils.getUsernameForAccount(mSavedAccount);
} else {
return null;
}
}
}
@@ -0,0 +1,480 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
* Copyright (C) 2012 Bartek Przybylski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common;
import android.accounts.AccountManager;
import android.accounts.AccountsException;
import android.net.Uri;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials;
import com.owncloud.android.lib.common.http.HttpClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import com.owncloud.android.lib.common.network.RedirectionPath;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.common.utils.RandomUtils;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import at.bitfire.dav4android.exception.HttpException;
import okhttp3.Cookie;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import static com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID;
public class OwnCloudClient extends HttpClient {
public static final String WEBDAV_FILES_PATH_4_0 = "/remote.php/dav/files/";
public static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/";
public static final String STATUS_PATH = "/status.php";
public static final String FILES_WEB_PATH = "/index.php/apps/files";
private static final String TAG = OwnCloudClient.class.getSimpleName();
private static final int MAX_REDIRECTIONS_COUNT = 3;
private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1;
private static final String PARAM_PROTOCOL_VERSION = "http.protocol.version";
private static byte[] sExhaustBuffer = new byte[1024];
private static int sIntanceCounter = 0;
private OwnCloudCredentials mCredentials = null;
private int mInstanceNumber = 0;
private Uri mBaseUri;
private OwnCloudVersion mVersion = null;
private OwnCloudAccount mAccount;
/**
* {@link @OwnCloudClientManager} holding a reference to this object and delivering it to callers; might be
* NULL
*/
private OwnCloudClientManager mOwnCloudClientManager = null;
private String mRedirectedLocation;
private boolean mFollowRedirects;
public OwnCloudClient(Uri baseUri) {
if (baseUri == null) {
throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL");
}
mBaseUri = baseUri;
mInstanceNumber = sIntanceCounter++;
Log_OC.d(TAG + " #" + mInstanceNumber, "Creating OwnCloudClient");
clearCredentials();
clearCookies();
}
public void setCredentials(OwnCloudCredentials credentials) {
if (credentials != null) {
mCredentials = credentials;
mCredentials.applyTo(this);
} else {
clearCredentials();
}
}
public void clearCredentials() {
if (!(mCredentials instanceof OwnCloudAnonymousCredentials)) {
mCredentials = OwnCloudCredentialsFactory.getAnonymousCredentials();
}
mCredentials.applyTo(this);
}
public void applyCredentials() {
mCredentials.applyTo(this);
}
public int executeHttpMethod (HttpBaseMethod method) throws Exception {
boolean repeatWithFreshCredentials;
int repeatCounter = 0;
int status;
do {
setRequestId(method);
status = method.execute();
checkFirstRedirection(method);
if(mFollowRedirects && !isIdPRedirection()) {
status = followRedirection(method).getLastStatus();
}
repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter);
if (repeatWithFreshCredentials) {
repeatCounter++;
}
} while (repeatWithFreshCredentials);
return status;
}
private void checkFirstRedirection(HttpBaseMethod method) {
final String location = method.getResponseHeader(HttpConstants.LOCATION_HEADER_LOWER);
if(location != null && !location.isEmpty()) {
mRedirectedLocation = location;
}
}
private int executeRedirectedHttpMethod (HttpBaseMethod method) throws Exception {
boolean repeatWithFreshCredentials;
int repeatCounter = 0;
int status;
do {
setRequestId(method);
status = method.execute();
repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter);
if (repeatWithFreshCredentials) {
repeatCounter++;
}
} while (repeatWithFreshCredentials);
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);
Log_OC.d(TAG, "Executing " + method.getClass().getSimpleName() + " in request with id " + requestId);
}
public RedirectionPath followRedirection(HttpBaseMethod method) throws Exception {
int redirectionsCount = 0;
int status = method.getStatusCode();
RedirectionPath redirectionPath = new RedirectionPath(status, MAX_REDIRECTIONS_COUNT);
while (redirectionsCount < MAX_REDIRECTIONS_COUNT &&
(status == HttpConstants.HTTP_MOVED_PERMANENTLY ||
status == HttpConstants.HTTP_MOVED_TEMPORARILY ||
status == HttpConstants.HTTP_TEMPORARY_REDIRECT)
) {
final String location = method.getResponseHeader(HttpConstants.LOCATION_HEADER) != null
? method.getResponseHeader(HttpConstants.LOCATION_HEADER)
: method.getResponseHeader(HttpConstants.LOCATION_HEADER_LOWER);
if (location != null) {
Log_OC.d(TAG + " #" + mInstanceNumber,
"Location to redirect: " + location);
redirectionPath.addLocation(location);
// Release the connection to avoid reach the max number of connections per host
// due to it will be set a different url
exhaustResponse(method.getResponseBodyAsStream());
method.setUrl(HttpUrl.parse(location));
final String destination = method.getRequestHeader("Destination") != null
? method.getRequestHeader("Destination")
: method.getRequestHeader("destination");
if (destination != null) {
final int suffixIndex = location.lastIndexOf(getUserFilesWebDavUri().toString());
final String redirectionBase = location.substring(0, suffixIndex);
final String destinationPath = destination.substring(mBaseUri.toString().length());
method.setRequestHeader("destination", redirectionBase + destinationPath);
}
try {
status = executeRedirectedHttpMethod(method);
} catch (HttpException e) {
if(e.getMessage().contains(Integer.toString(HttpConstants.HTTP_MOVED_TEMPORARILY))) {
status = HttpConstants.HTTP_MOVED_TEMPORARILY;
} else {
throw e;
}
}
redirectionPath.addStatus(status);
redirectionsCount++;
} else {
Log_OC.d(TAG + " #" + mInstanceNumber, "No location to redirect!");
status = HttpConstants.HTTP_NOT_FOUND;
}
}
return redirectionPath;
}
/**
* Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation.
*
* @param responseBodyAsStream InputStream with the HTTP response to exhaust.
*/
public void exhaustResponse(InputStream responseBodyAsStream) {
if (responseBodyAsStream != null) {
try {
while (responseBodyAsStream.read(sExhaustBuffer) >= 0) ;
responseBodyAsStream.close();
} catch (IOException io) {
Log_OC.e(TAG, "Unexpected exception while exhausting not interesting HTTP response;" +
" will be IGNORED", io);
}
}
}
public Uri getBaseFilesWebDavUri(){
return Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0);
}
public Uri getUserFilesWebDavUri() {
return mCredentials instanceof OwnCloudAnonymousCredentials
? Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0)
: Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0 + AccountUtils.getUserId(
mAccount.getSavedAccount(), getContext()
)
);
}
public Uri getUploadsWebDavUri() {
return mCredentials instanceof OwnCloudAnonymousCredentials
? Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0)
: Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0 + AccountUtils.getUserId(
mAccount.getSavedAccount(), getContext()
)
);
}
/**
* Sets the root URI to the ownCloud server.
*
* Use with care.
*
* @param uri
*/
public void setBaseUri(Uri uri) {
if (uri == null) {
throw new IllegalArgumentException("URI cannot be NULL");
}
mBaseUri = uri;
}
public Uri getBaseUri() {
return mBaseUri;
}
public final OwnCloudCredentials getCredentials() {
return mCredentials;
}
private void logCookie(Cookie cookie) {
Log_OC.d(TAG, "Cookie name: " + cookie.name());
Log_OC.d(TAG, " value: " + cookie.value());
Log_OC.d(TAG, " domain: " + cookie.domain());
Log_OC.d(TAG, " path: " + cookie.path());
Log_OC.d(TAG, " expiryDate: " + cookie.expiresAt());
Log_OC.d(TAG, " secure: " + cookie.secure());
}
private void logCookiesAtRequest(Headers headers, String when) {
int counter = 0;
for (final String cookieHeader : headers.toMultimap().get("cookie")) {
Log_OC.d(TAG + " #" + mInstanceNumber,
"Cookies at request (" + when + ") (" + counter++ + "): "
+ cookieHeader);
}
if (counter == 0) {
Log_OC.d(TAG + " #" + mInstanceNumber, "No cookie at request before");
}
}
private void logSetCookiesAtResponse(Headers headers) {
int counter = 0;
for (final String cookieHeader : headers.toMultimap().get("set-cookie")) {
Log_OC.d(TAG + " #" + mInstanceNumber,
"Set-Cookie (" + counter++ + "): " + cookieHeader);
}
if (counter == 0) {
Log_OC.d(TAG + " #" + mInstanceNumber, "No set-cookie");
}
}
public String getCookiesString() {
String cookiesString = "";
List<Cookie> cookieList = getCookiesFromUrl(HttpUrl.parse(mBaseUri.toString()));
if (cookieList != null) {
for (Cookie cookie : cookieList) {
cookiesString += cookie.toString() + ";";
}
}
return cookiesString;
}
public void setCookiesForCurrentAccount(List<Cookie> cookies) {
getOkHttpClient().cookieJar().saveFromResponse(
HttpUrl.parse(getAccount().getBaseUri().toString()),
cookies
);
}
public void setOwnCloudVersion(OwnCloudVersion version) {
mVersion = version;
}
public OwnCloudVersion getOwnCloudVersion() {
return mVersion;
}
public void setAccount(OwnCloudAccount account) {
this.mAccount = account;
}
public OwnCloudAccount getAccount() {
return mAccount;
}
/**
* Checks the status code of an execution and decides if should be repeated with fresh credentials.
*
* Invalidates current credentials if the request failed as anauthorized.
*
* Refresh current credentials if possible, and marks a retry.
*
* @param status
* @param repeatCounter
* @return
*/
private boolean checkUnauthorizedAccess(int status, int repeatCounter) {
boolean credentialsWereRefreshed = false;
if (shouldInvalidateAccountCredentials(status)) {
boolean invalidated = invalidateAccountCredentials();
if (invalidated) {
if (getCredentials().authTokenCanBeRefreshed() &&
repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) {
try {
mAccount.loadCredentials(getContext());
// if mAccount.getCredentials().length() == 0 --> refresh failed
setCredentials(mAccount.getCredentials());
credentialsWereRefreshed = true;
} catch (AccountsException | IOException e) {
Log_OC.e(
TAG,
"Error while trying to refresh auth token for " + mAccount.getSavedAccount().name,
e
);
}
}
if (!credentialsWereRefreshed && mOwnCloudClientManager != null) {
// if credentials are not refreshed, client must be removed
// from the OwnCloudClientManager to prevent it is reused once and again
mOwnCloudClientManager.removeClientFor(mAccount);
}
}
// else: onExecute will finish with status 401
}
return credentialsWereRefreshed;
}
/**
* Determines if credentials should be invalidated according the to the HTTPS status
* of a network request just performed.
*
* @param httpStatusCode Result of the last request ran with the 'credentials' belows.
* @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or
* cannot be invalidated with the given arguments.
*/
private boolean shouldInvalidateAccountCredentials(int httpStatusCode) {
boolean should = (httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED || isIdPRedirection()); // invalid credentials
should &= (mCredentials != null && // real credentials
!(mCredentials instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials));
// test if have all the needed to effectively invalidate ...
should &= (mAccount != null && mAccount.getSavedAccount() != null && getContext() != null);
return should;
}
/**
* Invalidates credentials stored for the given account in the system {@link AccountManager} and in
* current {@link OwnCloudClientManagerFactory#getDefaultSingleton()} instance.
*
* {@link #shouldInvalidateAccountCredentials(int)} should be called first.
*
* @return 'True' if invalidation was successful, 'false' otherwise.
*/
private boolean invalidateAccountCredentials() {
AccountManager am = AccountManager.get(getContext());
am.invalidateAuthToken(
mAccount.getSavedAccount().type,
mCredentials.getAuthToken()
);
am.clearPassword(mAccount.getSavedAccount()); // being strict, only needed for Basic Auth credentials
return true;
}
public OwnCloudClientManager getOwnCloudClientManager() {
return mOwnCloudClientManager;
}
void setOwnCloudClientManager(OwnCloudClientManager clientManager) {
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;
}
public void setFollowRedirects(boolean followRedirects) {
this.mFollowRedirects = followRedirects;
}
}
@@ -0,0 +1,160 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common;
import android.accounts.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
*
* 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.
*
* @param uri URL to the ownCloud server; BASE ENTRY POINT, not WebDavPATH
* @param context Android context where the OwnCloudClient is being created.
* @return A OwnCloudClient object ready to be used
*/
public static OwnCloudClient createOwnCloudClient(Uri uri, Context context,
boolean followRedirects) {
OwnCloudClient client = new OwnCloudClient(uri);
client.setFollowRedirects(followRedirects);
client.setContext(context);
return client;
}
}
@@ -0,0 +1,54 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common;
import java.io.IOException;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
/**
* Manager to create and reuse OwnCloudClient instances to access remote OC servers.
*
* @author David A. Velasco
* @author masensio
* @author Christian Schabesberger
*/
public interface OwnCloudClientManager {
OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws AccountNotFoundException,
OperationCanceledException, AuthenticatorException,
IOException;
OwnCloudClient removeClientFor(OwnCloudAccount account);
void saveAllClients(Context context, String accountType)
throws AccountNotFoundException, AuthenticatorException,
IOException, OperationCanceledException;
}
@@ -0,0 +1,103 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common;
public class OwnCloudClientManagerFactory {
public static enum Policy {
ALWAYS_NEW_CLIENT,
SINGLE_SESSION_PER_ACCOUNT,
SINGLE_SESSION_PER_ACCOUNT_IF_SERVER_SUPPORTS_SERVER_MONITORING
}
private static Policy sDefaultPolicy = Policy.ALWAYS_NEW_CLIENT;
private static OwnCloudClientManager sDefaultSingleton;
private static String sUserAgent;
public static OwnCloudClientManager newDefaultOwnCloudClientManager() {
return newOwnCloudClientManager(sDefaultPolicy);
}
public static OwnCloudClientManager newOwnCloudClientManager(Policy policy) {
switch (policy) {
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();
default:
throw new IllegalArgumentException("Unknown policy");
}
}
public static OwnCloudClientManager getDefaultSingleton() {
if (sDefaultSingleton == null) {
sDefaultSingleton = newDefaultOwnCloudClientManager();
}
return sDefaultSingleton;
}
public static Policy getDefaultPolicy() {
return sDefaultPolicy;
}
public static void setDefaultPolicy(Policy policy) {
if (policy == null) {
throw new IllegalArgumentException("Default policy cannot be NULL");
}
if (defaultSingletonMustBeUpdated(policy)) {
sDefaultSingleton = null;
}
sDefaultPolicy = policy;
}
public static void setUserAgent(String userAgent) {
sUserAgent = userAgent;
}
public static String getUserAgent() {
return sUserAgent;
}
private static boolean defaultSingletonMustBeUpdated(Policy policy) {
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;
}
}
@@ -0,0 +1,81 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.IOException;
public class SimpleFactoryManager implements OwnCloudClientManager {
private static final String TAG = SimpleFactoryManager.class.getSimpleName();
@Override
public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws
OperationCanceledException, AuthenticatorException, IOException {
Log_OC.d(TAG, "getClientFor(OwnCloudAccount ... : ");
OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(
account.getBaseUri(),
context.getApplicationContext(),
true);
Log_OC.v(TAG, " new client {" +
(account.getName() != null ?
account.getName() :
AccountUtils.buildAccountName(account.getBaseUri(), "")
) + ", " + client.hashCode() + "}");
if (account.getCredentials() == null) {
account.loadCredentials(context);
}
client.setCredentials(account.getCredentials());
client.setAccount(account);
client.setContext(context);
client.setOwnCloudClientManager(this);
return client;
}
@Override
public OwnCloudClient removeClientFor(OwnCloudAccount account) {
// nothing to do - not taking care of tracking instances!
return null;
}
@Override
public void saveAllClients(Context context, String accountType) {
// nothing to do - not taking care of tracking instances!
}
}
@@ -0,0 +1,238 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
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.utils.Log_OC;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Implementation of {@link OwnCloudClientManager}
*
* TODO check multithreading safety
*
* @author David A. Velasco
* @author masensio
* @author Christian Schabesberger
* @author David González Verdugo
*/
public class SingleSessionManager implements OwnCloudClientManager {
private static final String TAG = SingleSessionManager.class.getSimpleName();
private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername =
new ConcurrentHashMap<>();
private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername =
new ConcurrentHashMap<>();
@Override
public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) throws OperationCanceledException,
AuthenticatorException, IOException {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log_OC.d(TAG, "getClientFor starting ");
}
if (account == null) {
throw new IllegalArgumentException("Cannot get an OwnCloudClient for a null account");
}
OwnCloudClient client = null;
String accountName = account.getName();
String sessionName = account.getCredentials() == null ? "" :
AccountUtils.buildAccountName(
account.getBaseUri(),
account.getCredentials().getAuthToken());
if (accountName != null) {
client = mClientsWithKnownUsername.get(accountName);
}
boolean reusingKnown = false; // just for logs
if (client == null) {
if (accountName != null) {
client = mClientsWithUnknownUsername.remove(sessionName);
if (client != null) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log_OC.v(TAG, "reusing client for session " + sessionName);
}
mClientsWithKnownUsername.put(accountName, client);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log_OC.v(TAG, "moved client to account " + accountName);
}
}
} else {
client = mClientsWithUnknownUsername.get(sessionName);
}
} else {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log_OC.v(TAG, "reusing client for account " + accountName);
}
reusingKnown = true;
}
if (client == null) {
// no client to reuse - create a new one
client = OwnCloudClientFactory.createOwnCloudClient(
account.getBaseUri(),
context.getApplicationContext(),
true); // TODO remove dependency on OwnCloudClientFactory
client.setAccount(account);
client.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)) {
Log_OC.v(TAG, "new client for account " + accountName);
}
} else {
mClientsWithUnknownUsername.put(sessionName, client);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log_OC.v(TAG, "new client for session " + sessionName);
}
}
} else {
if (!reusingKnown && Log.isLoggable(TAG, Log.VERBOSE)) {
Log_OC.v(TAG, "reusing client for session " + sessionName);
}
keepCredentialsUpdated(client);
keepCookiesUpdated(context, account, client);
keepUriUpdated(account, client);
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log_OC.d(TAG, "getClientFor finishing ");
}
return client;
}
@Override
public OwnCloudClient removeClientFor(OwnCloudAccount account) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log_OC.d(TAG, "removeClientFor starting ");
}
if (account == null) {
return null;
}
OwnCloudClient client;
String accountName = account.getName();
if (accountName != null) {
client = mClientsWithKnownUsername.remove(accountName);
if (client != null) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log_OC.v(TAG, "Removed client for account " + accountName);
}
return client;
} else {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log_OC.v(TAG, "No client tracked for account " + accountName);
}
}
}
mClientsWithUnknownUsername.clear();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log_OC.d(TAG, "removeClientFor finishing ");
}
return null;
}
@Override
public void saveAllClients(Context context, String accountType) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log_OC.d(TAG, "Saving sessions... ");
}
Iterator<String> accountNames = mClientsWithKnownUsername.keySet().iterator();
String accountName;
Account account;
while (accountNames.hasNext()) {
accountName = accountNames.next();
account = new Account(accountName, accountType);
AccountUtils.saveClient(
mClientsWithKnownUsername.get(accountName),
account,
context);
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log_OC.d(TAG, "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) {
String recentCookies = am.getUserData(account.getSavedAccount(), AccountUtils.Constants.KEY_COOKIES);
String previousCookies = reusedClient.getCookiesString();
if (recentCookies != null && previousCookies != "" && !recentCookies.equals(previousCookies)) {
AccountUtils.restoreCookies(account.getSavedAccount(), reusedClient, context);
}
}
}
// this method is just a patch; we need to distinguish accounts in the same host but
// different paths; but that requires updating the accountNames for apps upgrading
private void keepUriUpdated(OwnCloudAccount account, OwnCloudClient reusedClient) {
Uri recentUri = account.getBaseUri();
if (!recentUri.equals(reusedClient.getBaseUri())) {
reusedClient.setBaseUri(recentUri);
}
}
}
@@ -0,0 +1,49 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.accounts;
/**
* @author masensio
* @author David A. Velasco
*/
public class AccountTypeUtils {
public static String getAuthTokenTypePass(String accountType) {
return accountType + ".password";
}
public static String getAuthTokenTypeAccessToken(String accountType) {
return accountType + ".oauth2.access_token";
}
public static String getAuthTokenTypeRefreshToken(String accountType) {
return accountType + ".oauth2.refresh_token";
}
public static String getAuthTokenTypeSamlSessionCookie(String accountType) {
return accountType + ".saml.web_sso.session_cookie";
}
}
@@ -0,0 +1,347 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
* Copyright (C) 2012 Bartek Przybylski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.accounts;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountsException;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.net.Uri;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.FileUtils;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.Cookie;
public class AccountUtils {
private static final String TAG = AccountUtils.class.getSimpleName();
/**
* Constructs full url to host and webdav resource basing on host version
*
* @param context Valid Android {@link Context}, needed to access the {@link AccountManager}
* @param account A stored ownCloud {@link Account}
* @return Full URL to WebDAV endpoint in the server corresponding to 'account'.
* @throws AccountNotFoundException When 'account' is unknown for the AccountManager
*/
public static String getWebDavUrlForAccount(Context context, Account account)
throws AccountNotFoundException {
String webDavUrlForAccount = "";
try {
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) {
e.printStackTrace();
}
return webDavUrlForAccount;
}
/**
* Extracts url server from the account
*
* @param context Valid Android {@link Context}, needed to access the {@link AccountManager}
* @param account A stored ownCloud {@link Account}
* @return Full URL to the server corresponding to 'account', ending in the base path
* common to all API endpoints.
* @throws AccountNotFoundException When 'account' is unknown for the AccountManager
*/
public static String getBaseUrlForAccount(Context context, Account account)
throws AccountNotFoundException {
AccountManager ama = AccountManager.get(context.getApplicationContext());
String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL);
if (baseurl == null)
throw new AccountNotFoundException(account, "Account not found", null);
return baseurl;
}
/**
* Get the username corresponding to an OC account.
*
* @param account An OC account
* @return Username for the given account, extracted from the account.name
*/
public static String getUsernameForAccount(Account account) {
String username = null;
try {
username = account.name.substring(0, account.name.lastIndexOf('@'));
} catch (Exception e) {
Log_OC.e(TAG, "Couldn't get a username for the given account", e);
}
return username;
}
/**
* Get the stored server version corresponding to an OC account.
*
* @param account An OC account
* @param context Application context
* @return Version of the OC server, according to last check
*/
public static OwnCloudVersion getServerVersionForAccount(Account account, Context context) {
AccountManager ama = AccountManager.get(context);
OwnCloudVersion version = null;
try {
String versionString = ama.getUserData(account, Constants.KEY_OC_VERSION);
version = new OwnCloudVersion(versionString);
} catch (Exception e) {
Log_OC.e(TAG, "Couldn't get a the server version for an account", e);
}
return version;
}
/**
* @return
* @throws IOException
* @throws AuthenticatorException
* @throws OperationCanceledException
*/
public static OwnCloudCredentials getCredentialsForAccount(Context context, Account account)
throws OperationCanceledException, AuthenticatorException, IOException {
OwnCloudCredentials credentials;
AccountManager am = AccountManager.get(context);
String supportsOAuth2 = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_OAUTH2);
boolean isOauth2 = supportsOAuth2 != null && supportsOAuth2.equals("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));
if (isOauth2) {
String accessToken = am.blockingGetAuthToken(
account,
AccountTypeUtils.getAuthTokenTypeAccessToken(account.type),
false);
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,
AccountTypeUtils.getAuthTokenTypePass(account.type),
false);
credentials = OwnCloudCredentialsFactory.newBasicCredentials(
username,
password,
version.isPreemptiveAuthenticationPreferred()
);
}
return credentials;
}
/**
* Get the user id corresponding to an OC account.
* @param account ownCloud account
* @return user id
*/
public static String getUserId(Account account, Context context) {
AccountManager accountMgr = AccountManager.get(context);
return accountMgr.getUserData(account, Constants.KEY_ID);
}
public static String buildAccountNameOld(Uri serverBaseUrl, String username) {
if (serverBaseUrl.getScheme() == null) {
serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString());
}
String accountName = username + "@" + serverBaseUrl.getHost();
if (serverBaseUrl.getPort() >= 0) {
accountName += ":" + serverBaseUrl.getPort();
}
return accountName;
}
public static String buildAccountName(Uri serverBaseUrl, String username) {
if (serverBaseUrl.getScheme() == null) {
serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString());
}
// Remove http:// or https://
String url = serverBaseUrl.toString();
if (url.contains("://")) {
url = url.substring(serverBaseUrl.toString().indexOf("://") + 3);
}
String accountName = username + "@" + url;
return accountName;
}
public static void saveClient(OwnCloudClient client, Account savedAccount, Context context) {
// Account Manager
AccountManager ac = AccountManager.get(context.getApplicationContext());
if (client != null) {
String cookiesString = client.getCookiesString();
if (!"".equals(cookiesString)) {
ac.setUserData(savedAccount, Constants.KEY_COOKIES, cookiesString);
Log_OC.d(TAG, "Saving Cookies: "+ cookiesString );
}
}
}
/**
* Restore the client cookies persisted in an account stored in the system AccountManager.
*
* @param account Stored account.
* @param client Client to restore cookies in.
* @param context Android context used to access the system AccountManager.
*/
public static void restoreCookies(Account account, OwnCloudClient client, Context context) {
if (account == null) {
Log_OC.d(TAG, "Cannot restore cookie for null account");
} else {
Log_OC.d(TAG, "Restoring cookies for " + account.name);
// Account Manager
AccountManager am = AccountManager.get(context.getApplicationContext());
Uri serverUri = (client.getBaseUri() != null) ? client.getBaseUri() : client.getUserFilesWebDavUri();
String cookiesString = am.getUserData(account, Constants.KEY_COOKIES);
if (cookiesString != null) {
String[] rawCookies = cookiesString.split(";");
List<Cookie> cookieList = new ArrayList<>(rawCookies.length);
for(String rawCookie : rawCookies) {
rawCookie = rawCookie.replace(" ", "");
final int equalPos = rawCookie.indexOf('=');
if (equalPos == -1) continue;
cookieList.add(new Cookie.Builder()
.name(rawCookie.substring(0, equalPos))
.value(rawCookie.substring(equalPos + 1))
.domain(serverUri.getHost())
.path(
serverUri.getPath().equals("")
? FileUtils.PATH_SEPARATOR
: serverUri.getPath()
)
.build());
}
client.setCookiesForCurrentAccount(cookieList);
}
}
}
public static class AccountNotFoundException extends AccountsException {
/**
* Generated - should be refreshed every time the class changes!!
*/
private static final long serialVersionUID = -1684392454798508693L;
private Account mFailedAccount;
public AccountNotFoundException(Account failedAccount, String message, Throwable cause) {
super(message, cause);
mFailedAccount = failedAccount;
}
public Account getFailedAccount() {
return mFailedAccount;
}
}
public static class Constants {
/**
* Version should be 3 numbers separated by dot so it can be parsed by
* {@link OwnCloudVersion}
*/
public static final String KEY_OC_VERSION = "oc_version";
/**
* Base url should point to owncloud installation without trailing / ie:
* http://server/path or https://owncloud.server
*/
public static final String KEY_OC_BASE_URL = "oc_base_url";
/**
* Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
*/
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
*/
public static final String KEY_COOKIES = "oc_account_cookies";
/**
* OC account version
*/
public static final String KEY_OC_ACCOUNT_VERSION = "oc_account_version";
/**
* User's id
*/
public static final String KEY_ID = "oc_id";
/**
* User's display name
*/
public static final String KEY_DISPLAY_NAME = "oc_display_name";
/**
* OAuth2 refresh token
**/
public static final String KEY_OAUTH2_REFRESH_TOKEN = "oc_oauth2_refresh_token";
}
}
@@ -0,0 +1,78 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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;
import okhttp3.Credentials;
public class OwnCloudBasicCredentials implements OwnCloudCredentials {
private static final String TAG = OwnCloudCredentials.class.getSimpleName();
private String mUsername;
private String mPassword;
public OwnCloudBasicCredentials(String username, String password) {
mUsername = username != null ? username : "";
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));
}
@Override
public String getUsername() {
return mUsername;
}
@Override
public String getAuthToken() {
return mPassword;
}
@Override
public boolean authTokenExpires() {
return false;
}
@Override
public boolean authTokenCanBeRefreshed() {
return false;
}
}
@@ -0,0 +1,70 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 OwnCloudBearerCredentials implements OwnCloudCredentials {
private String mUsername;
private String mAccessToken;
public OwnCloudBearerCredentials(String username, String accessToken) {
mUsername = username != null ? username : "";
mAccessToken = accessToken != null ? accessToken : "";
}
@Override
public 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
return mUsername;
}
@Override
public String getAuthToken() {
return mAccessToken;
}
@Override
public boolean authTokenExpires() {
return true;
}
@Override
public boolean authTokenCanBeRefreshed() {
return true;
}
}
@@ -0,0 +1,40 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication;
import com.owncloud.android.lib.common.OwnCloudClient;
public interface OwnCloudCredentials {
void applyTo(OwnCloudClient ownCloudClient);
String getUsername();
String getAuthToken();
boolean authTokenExpires();
boolean authTokenCanBeRefreshed();
}
@@ -0,0 +1,95 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 OwnCloudCredentialsFactory {
public static final String CREDENTIAL_CHARSET = "UTF-8";
private static OwnCloudAnonymousCredentials sAnonymousCredentials;
public static OwnCloudCredentials newBasicCredentials(String username, String password) {
return new OwnCloudBasicCredentials(username, password);
}
public static OwnCloudCredentials newBasicCredentials(
String username, String password, boolean preemptiveMode
) {
return new OwnCloudBasicCredentials(username, password, preemptiveMode);
}
public static OwnCloudCredentials newBearerCredentials(String username, String authToken) {
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();
}
return sAnonymousCredentials;
}
public static final class OwnCloudAnonymousCredentials implements OwnCloudCredentials {
protected OwnCloudAnonymousCredentials() {
}
@Override
public void applyTo(OwnCloudClient client) {
// Clear previous basic credentials
HttpClient.deleteHeaderForAllRequests(HttpConstants.AUTHORIZATION_HEADER);
HttpClient.deleteHeaderForAllRequests(HttpConstants.COOKIE_HEADER);
}
@Override
public String getAuthToken() {
return "";
}
@Override
public boolean authTokenExpires() {
return false;
}
@Override
public boolean authTokenCanBeRefreshed() {
return false;
}
@Override
public String getUsername() {
// no user name
return null;
}
}
}
@@ -0,0 +1,70 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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;
}
}
@@ -0,0 +1,98 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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.oauth;
/**
* @author David A. Velasco
* @author Christian Schabesberger
*/
public class BearerCredentials {
public static final int HASH_SEED = 17;
public static final int HASH_OFFSET = 37;
private String mAccessToken;
/**
* The constructor with the bearer token
*
* @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;
}
/**
* Returns the access token
*
* @return The access token
*/
public String getAccessToken() {
return mAccessToken;
}
/**
* Get this object string.
*
* @return The access token
*/
public String toString() {
return mAccessToken;
}
/**
* Does a hash of the access token.
*
* @return The hash code of the access token
*/
public int hashCode() {
return HASH_SEED * HASH_OFFSET + mAccessToken.hashCode();
}
/**
* These credentials are assumed equal if accessToken is the same.
*
* @param o The other object to compare with.
*
* @return 'True' if the object is equivalent.
*/
public boolean equals(Object o) {
if (o == null) return false;
if (this == o) return true;
if (this.getClass().equals(o.getClass())) {
BearerCredentials that = (BearerCredentials) o;
if (mAccessToken.equals(that.mAccessToken)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,66 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication.oauth;
public class OAuth2ClientConfiguration {
private String mClientId;
private String mClientSecret;
private String mRedirectUri;
public OAuth2ClientConfiguration(String clientId, String clientSecret, String redirectUri) {
mClientId = (clientId == null) ? "" : clientId;
mClientSecret = (clientSecret == null) ? "" : clientSecret;
mRedirectUri = (redirectUri == null) ? "" : redirectUri;
}
public String getClientId() {
return mClientId;
}
public void setClientId(String clientId) {
mClientId = (clientId == null) ? "" : clientId;
}
public String getClientSecret() {
return mClientSecret;
}
public void setClientSecret(String clientSecret) {
mClientSecret = (clientSecret == null) ? "" : clientSecret;
}
public String getRedirectUri() {
return mRedirectUri;
}
public void setRedirectUri(String redirectUri) {
this.mRedirectUri = (redirectUri == null) ? "" : redirectUri;
}
}
@@ -0,0 +1,68 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication.oauth;
/**
* Constant values for OAuth 2 protocol.
*
* Includes required and optional parameter NAMES used in the 'authorization code' grant type.
*/
public class OAuth2Constants {
/// Parameters to send to the Authorization Endpoint
public static final String KEY_RESPONSE_TYPE = "response_type";
public static final String KEY_REDIRECT_URI = "redirect_uri";
public static final String KEY_CLIENT_ID = "client_id";
public static final String KEY_SCOPE = "scope";
public static final String KEY_STATE = "state";
/// Additional parameters to send to the Token Endpoint
public static final String KEY_GRANT_TYPE = "grant_type";
public static final String KEY_CODE = "code";
// Used to get the Access Token using Refresh Token
public static final String OAUTH2_REFRESH_TOKEN_GRANT_TYPE = "refresh_token";
/// Parameters received in an OK response from the Token Endpoint
public static final String KEY_ACCESS_TOKEN = "access_token";
public static final String KEY_TOKEN_TYPE = "token_type";
public static final String KEY_EXPIRES_IN = "expires_in";
public static final String KEY_REFRESH_TOKEN = "refresh_token";
/// Parameters in an ERROR response
public static final String KEY_ERROR = "error";
public static final String KEY_ERROR_DESCRIPTION = "error_description";
public static final String KEY_ERROR_URI = "error_uri";
public static final String VALUE_ERROR_ACCESS_DENIED = "access_denied";
/// Extra not standard
public static final String KEY_USER_ID = "user_id";
/// Depends on oauth2 grant type
public static final String OAUTH2_RESPONSE_TYPE_CODE = "code";
}
@@ -0,0 +1,146 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
* @author Christian Schabesberger
* Copyright (C) 2018 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.oauth;
import android.net.Uri;
import com.owncloud.android.lib.common.authentication.OwnCloudBasicCredentials;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.http.methods.nonwebdav.PostMethod;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import org.json.JSONObject;
import java.net.URL;
import java.util.Map;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
public class OAuth2GetAccessTokenOperation extends RemoteOperation<Map<String, String>> {
private String mGrantType;
private String mCode;
private String mClientId;
private String mClientSecret;
private String mRedirectUri;
private final String mAccessTokenEndpointPath;
private final OAuth2ResponseParser mResponseParser;
public OAuth2GetAccessTokenOperation(
String grantType,
String code,
String clientId,
String secretId,
String redirectUri,
String accessTokenEndpointPath
) {
mClientId = clientId;
mClientSecret = secretId;
mRedirectUri = redirectUri;
mGrantType = grantType;
mCode = code;
mAccessTokenEndpointPath =
accessTokenEndpointPath != null ?
accessTokenEndpointPath :
OwnCloudOAuth2Provider.ACCESS_TOKEN_ENDPOINT_PATH
;
mResponseParser = new OAuth2ResponseParser();
}
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult<Map<String, String>> result = null;
try {
final RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(OAuth2Constants.KEY_GRANT_TYPE, mGrantType)
.addFormDataPart(OAuth2Constants.KEY_CODE, mCode)
.addFormDataPart(OAuth2Constants.KEY_REDIRECT_URI, mRedirectUri)
.addFormDataPart(OAuth2Constants.KEY_CLIENT_ID, mClientId)
.build();
Uri.Builder uriBuilder = client.getBaseUri().buildUpon();
uriBuilder.appendEncodedPath(mAccessTokenEndpointPath);
final PostMethod postMethod = new PostMethod(new URL(
client.getBaseUri().buildUpon()
.appendEncodedPath(mAccessTokenEndpointPath)
.build()
.toString()));
postMethod.setRequestBody(requestBody);
OwnCloudCredentials oauthCredentials =
new OwnCloudBasicCredentials(mClientId, mClientSecret);
OwnCloudCredentials oldCredentials = switchClientCredentials(oauthCredentials);
client.executeHttpMethod(postMethod);
switchClientCredentials(oldCredentials);
String response = postMethod.getResponseBodyAsString();
if (response != null && response.length() > 0) {
JSONObject tokenJson = new JSONObject(response);
Map<String, String> accessTokenResult =
mResponseParser.parseAccessTokenResult(tokenJson);
if (accessTokenResult.get(OAuth2Constants.KEY_ERROR) != null ||
accessTokenResult.get(OAuth2Constants.KEY_ACCESS_TOKEN) == null) {
result = new RemoteOperationResult<>(ResultCode.OAUTH2_ERROR);
} else {
result = new RemoteOperationResult<>(ResultCode.OK);
result.setData(accessTokenResult);
}
} else {
result = new RemoteOperationResult<>(ResultCode.OK);
client.exhaustResponse(postMethod.getResponseBodyAsStream());
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
}
return result;
}
private OwnCloudCredentials switchClientCredentials(OwnCloudCredentials newCredentials) {
OwnCloudCredentials previousCredentials = getClient().getCredentials();
getClient().setCredentials(newCredentials);
return previousCredentials;
}
}
@@ -0,0 +1,46 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication.oauth;
public enum OAuth2GrantType {
AUTHORIZATION_CODE("authorization_code"),
IMPLICIT("implicit"),
PASSWORD("password"),
CLIENT_CREDENTIAL("client_credentials"),
REFRESH_TOKEN("refresh_token") // not a grant type conceptually, but used as such to refresh access tokens
;
private String mValue;
OAuth2GrantType(String value) {
mValue = value;
}
public String getValue() {
return mValue;
}
}
@@ -0,0 +1,66 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication.oauth;
public interface OAuth2Provider {
/**
* {@link OAuth2RequestBuilder} implementation for this provider.
*
* @return {@link OAuth2RequestBuilder} implementation.
*/
OAuth2RequestBuilder getOperationBuilder();
/**
* Set configuration of the client that will use this {@link OAuth2Provider}
* @param oAuth2ClientConfiguration Configuration of the client that will use this {@link OAuth2Provider}
*/
void setClientConfiguration(OAuth2ClientConfiguration oAuth2ClientConfiguration);
/**
* Configuration of the client that is using this {@link OAuth2Provider}
* return Configuration of the client that is usinng this {@link OAuth2Provider}
*/
OAuth2ClientConfiguration getClientConfiguration();
/**
* Set base URI to authorization server.
*
* @param authorizationServerUri Set base URL to authorization server.
*/
void setAuthorizationServerUri(String authorizationServerUri);
/**
* base URI to authorization server.
*
* @return Base URL to authorization server.
*/
String getAuthorizationServerUri();
}
@@ -0,0 +1,122 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication.oauth;
import java.util.HashMap;
import java.util.Map;
public class OAuth2ProvidersRegistry {
private Map<String, OAuth2Provider> mProviders = new HashMap<>();
private OAuth2Provider mDefaultProvider = null;
private OAuth2ProvidersRegistry () {
}
/**
* See https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
*/
private static class LazyHolder {
private static final OAuth2ProvidersRegistry INSTANCE = new OAuth2ProvidersRegistry();
}
/**
* Singleton accesor.
*
* @return Singleton isntance of {@link OAuth2ProvidersRegistry}
*/
public static OAuth2ProvidersRegistry getInstance() {
return LazyHolder.INSTANCE;
}
/**
* Register an {@link OAuth2Provider} with the name passed as parameter.
*
* @param name Name to bind 'oAuthProvider' in the registry.
* @param oAuth2Provider An {@link OAuth2Provider} instance to keep in the registry.
* @throws IllegalArgumentException if 'name' or 'oAuthProvider' are null.
*/
public void registerProvider(String name, OAuth2Provider oAuth2Provider) {
if (name == null) {
throw new IllegalArgumentException("Name must not be NULL");
}
if (oAuth2Provider == null) {
throw new IllegalArgumentException("oAuth2Provider must not be NULL");
}
mProviders.put(name, oAuth2Provider);
if (mProviders.size() == 1) {
mDefaultProvider = oAuth2Provider;
}
}
public OAuth2Provider unregisterProvider(String name) {
OAuth2Provider unregisteredProvider = mProviders.remove(name);
if (mProviders.size() == 0) {
mDefaultProvider = null;
} else if (unregisteredProvider != null && unregisteredProvider == mDefaultProvider) {
mDefaultProvider = mProviders.values().iterator().next();
}
return unregisteredProvider;
}
/**
* Get default {@link OAuth2Provider}.
*
* @return Default provider, or NULL if there is no provider.
*/
public OAuth2Provider getProvider() {
return mDefaultProvider;
}
/**
* Get {@link OAuth2Provider} registered with the name passed as parameter.
*
* @param name Name used to register the desired {@link OAuth2Provider}
* @return {@link OAuth2Provider} registered with the name 'name'
*/
public OAuth2Provider getProvider(String name) {
return mProviders.get(name);
}
/**
* Sets the {@link OAuth2Provider} registered with the name passed as parameter as the default provider
*
* @param name Name used to register the {@link OAuth2Provider} to set as default.
* @return {@link OAuth2Provider} set as default, or NULL if no provider was registered with 'name'.
*/
public OAuth2Provider setDefaultProvider(String name) {
OAuth2Provider toDefault = mProviders.get(name);
if (toDefault != null) {
mDefaultProvider = toDefault;
}
return toDefault;
}
}
@@ -0,0 +1,75 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication.oauth;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.util.HashMap;
import java.util.Map;
public class OAuth2QueryParser {
private static final String TAG = OAuth2QueryParser.class.getName();
private Map<String, String> mOAuth2ParsedAuthorizationResponse;
public OAuth2QueryParser() {
mOAuth2ParsedAuthorizationResponse = new HashMap<>();
}
public Map<String, String> parse(String query) {
mOAuth2ParsedAuthorizationResponse.clear();
if (query != null) {
String[] pairs = query.split("&");
int i = 0;
String key = "";
String value;
while (pairs.length > i) {
int j = 0;
String[] part = pairs[i].split("=");
while (part.length > j) {
String p = part[j];
if (j == 0) {
key = p;
} else if (j == 1) {
value = p;
mOAuth2ParsedAuthorizationResponse.put(key, value);
}
Log_OC.v(TAG, "[" + i + "," + j + "] = " + p);
j++;
}
i++;
}
}
return mOAuth2ParsedAuthorizationResponse;
}
}
@@ -0,0 +1,134 @@
/**
* ownCloud Android client application
*
* @author David González Verdugo
* @author Christian Schabesberger
*
* Copyright (C) 2018 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.common.authentication.oauth;
import android.net.Uri;
import com.owncloud.android.lib.common.authentication.OwnCloudBasicCredentials;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
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 org.json.JSONObject;
import java.net.URL;
import java.util.Map;
import com.owncloud.android.lib.common.http.methods.nonwebdav.PostMethod;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
public class OAuth2RefreshAccessTokenOperation extends RemoteOperation<Map<String, String>> {
private static final String TAG = OAuth2RefreshAccessTokenOperation.class.getSimpleName();
private String mClientId;
private String mClientSecret;
private String mRefreshToken;
private final String mAccessTokenEndpointPath;
private final OAuth2ResponseParser mResponseParser;
public OAuth2RefreshAccessTokenOperation(
String clientId,
String secretId,
String refreshToken,
String accessTokenEndpointPath
) {
mClientId = clientId;
mClientSecret = secretId;
mRefreshToken = refreshToken;
mAccessTokenEndpointPath =
accessTokenEndpointPath != null ?
accessTokenEndpointPath :
OwnCloudOAuth2Provider.ACCESS_TOKEN_ENDPOINT_PATH
;
mResponseParser = new OAuth2ResponseParser();
}
@Override
protected RemoteOperationResult<Map<String, String>> run(OwnCloudClient client) {
try {
final RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(OAuth2Constants.KEY_GRANT_TYPE,
OAuth2GrantType.REFRESH_TOKEN.getValue())
.addFormDataPart(OAuth2Constants.KEY_CLIENT_ID, mClientId)
.addFormDataPart(OAuth2Constants.KEY_REFRESH_TOKEN, mRefreshToken)
.build();
Uri.Builder uriBuilder = client.getBaseUri().buildUpon();
uriBuilder.appendEncodedPath(mAccessTokenEndpointPath);
final PostMethod postMethod = new PostMethod(new URL(
client.getBaseUri().buildUpon()
.appendEncodedPath(mAccessTokenEndpointPath)
.build()
.toString()));
postMethod.setRequestBody(requestBody);
final OwnCloudCredentials oauthCredentials = new OwnCloudBasicCredentials(mClientId, mClientSecret);
final OwnCloudCredentials oldCredentials = switchClientCredentials(oauthCredentials);
client.executeHttpMethod(postMethod);
switchClientCredentials(oldCredentials);
final String responseData = postMethod.getResponseBodyAsString();
Log_OC.d(TAG, "OAUTH2: raw response from POST TOKEN: " + responseData);
if (responseData != null && responseData.length() > 0) {
final JSONObject tokenJson = new JSONObject(responseData);
final Map<String, String> accessTokenResult =
mResponseParser.parseAccessTokenResult(tokenJson);
final RemoteOperationResult<Map<String, String>> result = new RemoteOperationResult<>(ResultCode.OK);
result.setData(accessTokenResult);
return (accessTokenResult.get(OAuth2Constants.KEY_ERROR) != null ||
accessTokenResult.get(OAuth2Constants.KEY_ACCESS_TOKEN) == null)
? new RemoteOperationResult<>(ResultCode.OAUTH2_ERROR)
: result;
} else {
return new RemoteOperationResult<>(postMethod);
}
} catch (Exception e) {
return new RemoteOperationResult<>(e);
}
}
private OwnCloudCredentials switchClientCredentials(OwnCloudCredentials newCredentials) {
OwnCloudCredentials previousCredentials = getClient().getCredentials();
getClient().setCredentials(newCredentials);
return previousCredentials;
}
}
@@ -0,0 +1,48 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication.oauth;
import com.owncloud.android.lib.common.operations.RemoteOperation;
public interface OAuth2RequestBuilder {
enum OAuthRequest {
GET_AUTHORIZATION_CODE, CREATE_ACCESS_TOKEN, REFRESH_ACCESS_TOKEN
}
void setRequest(OAuthRequest operation);
void setGrantType(OAuth2GrantType grantType);
void setAuthorizationCode(String code);
void setRefreshToken(String refreshToken);
RemoteOperation buildOperation();
String buildUri();
}
@@ -0,0 +1,77 @@
/**
* ownCloud Android client application
*
* @author David A. Velasco
*
* Copyright (C) 2017 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.common.authentication.oauth;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
class OAuth2ResponseParser {
Map<String, String> parseAccessTokenResult(JSONObject tokenJson) throws JSONException {
Map<String, String> resultTokenMap = new HashMap<>();
if (tokenJson.has(OAuth2Constants.KEY_ACCESS_TOKEN)) {
resultTokenMap.put(OAuth2Constants.KEY_ACCESS_TOKEN, tokenJson.
getString(OAuth2Constants.KEY_ACCESS_TOKEN));
}
if (tokenJson.has(OAuth2Constants.KEY_TOKEN_TYPE)) {
resultTokenMap.put(OAuth2Constants.KEY_TOKEN_TYPE, tokenJson.
getString(OAuth2Constants.KEY_TOKEN_TYPE));
}
if (tokenJson.has(OAuth2Constants.KEY_EXPIRES_IN)) {
resultTokenMap.put(OAuth2Constants.KEY_EXPIRES_IN, tokenJson.
getString(OAuth2Constants.KEY_EXPIRES_IN));
}
if (tokenJson.has(OAuth2Constants.KEY_REFRESH_TOKEN)) {
resultTokenMap.put(OAuth2Constants.KEY_REFRESH_TOKEN, tokenJson.
getString(OAuth2Constants.KEY_REFRESH_TOKEN));
}
if (tokenJson.has(OAuth2Constants.KEY_SCOPE)) {
resultTokenMap.put(OAuth2Constants.KEY_SCOPE, tokenJson.
getString(OAuth2Constants.KEY_SCOPE));
}
if (tokenJson.has(OAuth2Constants.KEY_ERROR)) {
resultTokenMap.put(OAuth2Constants.KEY_ERROR, tokenJson.
getString(OAuth2Constants.KEY_ERROR));
}
if (tokenJson.has(OAuth2Constants.KEY_ERROR_DESCRIPTION)) {
resultTokenMap.put(OAuth2Constants.KEY_ERROR_DESCRIPTION, tokenJson.
getString(OAuth2Constants.KEY_ERROR_DESCRIPTION));
}
if (tokenJson.has(OAuth2Constants.KEY_ERROR_URI)) {
resultTokenMap.put(OAuth2Constants.KEY_ERROR_URI, tokenJson.
getString(OAuth2Constants.KEY_ERROR_URI));
}
if (tokenJson.has(OAuth2Constants.KEY_USER_ID)) { // not standard
resultTokenMap.put(OAuth2Constants.KEY_USER_ID, tokenJson.
getString(OAuth2Constants.KEY_USER_ID));
}
return resultTokenMap;
}
}
@@ -0,0 +1,94 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication.oauth;
import com.owncloud.android.lib.common.utils.Log_OC;
public class OwnCloudOAuth2Provider implements OAuth2Provider {
public static final String NAME = OAuth2Provider.class.getName();
public static final String ACCESS_TOKEN_ENDPOINT_PATH = "index.php/apps/oauth2/api/v1/token";
private static final String AUTHORIZATION_CODE_ENDPOINT_PATH = "index.php/apps/oauth2/authorize";
private String mAuthorizationServerUrl = "";
private String mAccessTokenEndpointPath = ACCESS_TOKEN_ENDPOINT_PATH;
private String mAuthorizationCodeEndpointPath = AUTHORIZATION_CODE_ENDPOINT_PATH;
private OAuth2ClientConfiguration mClientConfiguration;
@Override
public OAuth2RequestBuilder getOperationBuilder() {
return new OwnCloudOAuth2RequestBuilder(this);
}
@Override
public void setClientConfiguration(OAuth2ClientConfiguration oAuth2ClientConfiguration) {
mClientConfiguration = oAuth2ClientConfiguration;
}
@Override
public OAuth2ClientConfiguration getClientConfiguration() {
return mClientConfiguration;
}
@Override
public void setAuthorizationServerUri(String authorizationServerUri) {
mAuthorizationServerUrl = authorizationServerUri;
}
@Override
public String getAuthorizationServerUri() {
return mAuthorizationServerUrl;
}
public String getAccessTokenEndpointPath() {
return mAccessTokenEndpointPath;
}
public void setAccessTokenEndpointPath(String accessTokenEndpointPath) {
if (accessTokenEndpointPath == null || accessTokenEndpointPath.length() <= 0) {
Log_OC.w(NAME, "Setting invalid access token endpoint path, going on with default");
mAccessTokenEndpointPath = ACCESS_TOKEN_ENDPOINT_PATH;
} else {
mAccessTokenEndpointPath = accessTokenEndpointPath;
}
}
public String getAuthorizationCodeEndpointPath() {
return mAuthorizationCodeEndpointPath;
}
public void setAuthorizationCodeEndpointPath(String authorizationCodeEndpointPath) {
if (authorizationCodeEndpointPath == null || authorizationCodeEndpointPath.length() <= 0) {
Log_OC.w(NAME, "Setting invalid authorization code endpoint path, going on with default");
mAuthorizationCodeEndpointPath = AUTHORIZATION_CODE_ENDPOINT_PATH;
} else {
mAuthorizationCodeEndpointPath = authorizationCodeEndpointPath;
}
}
}
@@ -0,0 +1,156 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.authentication.oauth;
import android.net.Uri;
import com.owncloud.android.lib.common.operations.RemoteOperation;
public class OwnCloudOAuth2RequestBuilder implements OAuth2RequestBuilder {
private OwnCloudOAuth2Provider mOAuth2Provider;
private OAuthRequest mRequest;
private OAuth2GrantType mGrantType = OAuth2GrantType.AUTHORIZATION_CODE;
private String mCode;
private String mRefreshToken;
public OwnCloudOAuth2RequestBuilder(OwnCloudOAuth2Provider ownCloudOAuth2Provider) {
mOAuth2Provider = ownCloudOAuth2Provider;
}
@Override
public void setRequest(OAuthRequest request) {
mRequest = request;
}
@Override
public void setGrantType(OAuth2GrantType grantType) {
mGrantType = grantType;
}
@Override
public void setAuthorizationCode(String code) {
mCode = code;
}
@Override
public void setRefreshToken(String refreshToken) {
mRefreshToken = refreshToken;
}
@Override
public RemoteOperation buildOperation() {
if (mGrantType != OAuth2GrantType.AUTHORIZATION_CODE &&
mGrantType != OAuth2GrantType.REFRESH_TOKEN) {
throw new UnsupportedOperationException(
"Unsupported grant type. Only " +
OAuth2GrantType.AUTHORIZATION_CODE.getValue() + " and " +
OAuth2GrantType.REFRESH_TOKEN + " are supported"
);
}
OAuth2ClientConfiguration clientConfiguration = mOAuth2Provider.getClientConfiguration();
switch(mRequest) {
case CREATE_ACCESS_TOKEN:
return new OAuth2GetAccessTokenOperation(
mGrantType.getValue(),
mCode,
clientConfiguration.getClientId(),
clientConfiguration.getClientSecret(),
clientConfiguration.getRedirectUri(),
mOAuth2Provider.getAccessTokenEndpointPath()
);
case REFRESH_ACCESS_TOKEN:
return new OAuth2RefreshAccessTokenOperation(
clientConfiguration.getClientId(),
clientConfiguration.getClientSecret(),
mRefreshToken,
mOAuth2Provider.getAccessTokenEndpointPath()
);
default:
throw new UnsupportedOperationException(
"Unsupported request"
);
}
}
@Override
public String buildUri() {
if (OAuth2GrantType.AUTHORIZATION_CODE != mGrantType) {
throw new UnsupportedOperationException(
"Unsupported grant type. Only " +
OAuth2GrantType.AUTHORIZATION_CODE.getValue() + " is supported by this provider"
);
}
OAuth2ClientConfiguration clientConfiguration = mOAuth2Provider.getClientConfiguration();
Uri uri;
Uri.Builder uriBuilder;
switch(mRequest) {
case GET_AUTHORIZATION_CODE:
uri = Uri.parse(mOAuth2Provider.getAuthorizationServerUri());
uriBuilder = uri.buildUpon();
uriBuilder.appendEncodedPath(mOAuth2Provider.getAuthorizationCodeEndpointPath());
uriBuilder.appendQueryParameter(
OAuth2Constants.KEY_RESPONSE_TYPE, OAuth2Constants.OAUTH2_RESPONSE_TYPE_CODE
);
uriBuilder.appendQueryParameter(
OAuth2Constants.KEY_REDIRECT_URI, clientConfiguration.getRedirectUri()
);
uriBuilder.appendQueryParameter(
OAuth2Constants.KEY_CLIENT_ID, clientConfiguration.getClientId()
);
uri = uriBuilder.build();
return uri.toString();
case CREATE_ACCESS_TOKEN:
uri = Uri.parse(mOAuth2Provider.getAuthorizationServerUri());
uriBuilder = uri.buildUpon();
uriBuilder.appendEncodedPath(mOAuth2Provider.getAccessTokenEndpointPath());
uriBuilder.appendQueryParameter(
OAuth2Constants.KEY_RESPONSE_TYPE, OAuth2Constants.OAUTH2_RESPONSE_TYPE_CODE
);
uriBuilder.appendQueryParameter(
OAuth2Constants.KEY_REDIRECT_URI, clientConfiguration.getRedirectUri()
);
uriBuilder.appendQueryParameter(
OAuth2Constants.KEY_CLIENT_ID, clientConfiguration.getClientId()
);
uri = uriBuilder.build();
return uri.toString();
default:
throw new UnsupportedOperationException(
"Unsupported request"
);
}
}
}
@@ -0,0 +1,172 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http;
import android.content.Context;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
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 com.owncloud.android.lib.common.utils.Log_OC;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
/**
* Client used to perform network operations
* @author David González Verdugo
*/
public class HttpClient {
private static final String TAG = HttpClient.class.toString();
private static OkHttpClient sOkHttpClient;
private static HttpInterceptor sOkHttpInterceptor;
private static Context sContext;
private static HashMap<String, List<Cookie>> sCookieStore = new HashMap<>();
public static void setContext(Context context) {
sContext = context;
}
public Context getContext() {
return sContext;
}
public static OkHttpClient getOkHttpClient() {
if (sOkHttpClient == null) {
try {
final X509TrustManager trustManager = new AdvancedX509TrustManager(
NetworkUtils.getKnownServersStore(sContext));
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] {trustManager}, null);
// Automatic cookie handling, NOT PERSISTENT
CookieJar cookieJar = new CookieJar() {
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
// Avoid duplicated cookies
Set<Cookie> nonDuplicatedCookiesSet = new HashSet<>();
nonDuplicatedCookiesSet.addAll(cookies);
List<Cookie> nonDuplicatedCookiesList = new ArrayList<>();
nonDuplicatedCookiesList.addAll(nonDuplicatedCookiesSet);
sCookieStore.put(url.host(), nonDuplicatedCookiesList);
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies = sCookieStore.get(url.host());
return cookies != null ? cookies : new ArrayList<>();
}
};
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
.addInterceptor(getOkHttpInterceptor())
.protocols(Arrays.asList(Protocol.HTTP_1_1))
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
.connectTimeout(HttpConstants.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
.followRedirects(false)
.sslSocketFactory(sslContext.getSocketFactory(), trustManager)
.hostnameVerifier((asdf, usdf) -> true)
.cookieJar(cookieJar);
// TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok.
//.hostnameVerifier(new BrowserCompatHostnameVerifier());
sOkHttpClient = clientBuilder.build();
} catch (Exception e) {
Log_OC.e(TAG, "Could not setup SSL system.", e);
}
}
return sOkHttpClient;
}
private static HttpInterceptor getOkHttpInterceptor() {
if (sOkHttpInterceptor == null) {
sOkHttpInterceptor = new HttpInterceptor();
addHeaderForAllRequests(HttpConstants.USER_AGENT_HEADER, OwnCloudClientManagerFactory.getUserAgent());
addHeaderForAllRequests(HttpConstants.PARAM_SINGLE_COOKIE_HEADER, "true");
addHeaderForAllRequests(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
}
return sOkHttpInterceptor;
}
public void disableAutomaticCookiesHandling() {
OkHttpClient.Builder clientBuilder = getOkHttpClient().newBuilder();
clientBuilder.cookieJar(new CookieJar() {
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
// DO NOTHING
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
return new ArrayList<>();
}
});
sOkHttpClient = clientBuilder.build();
}
/**
* 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) {
getOkHttpInterceptor()
.addRequestInterceptor(
new RequestHeaderInterceptor(headerName, headerValue)
);
}
public static void deleteHeaderForAllRequests(String headerName) {
getOkHttpInterceptor().deleteRequestHeaderInterceptor(headerName);
}
public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
return sCookieStore.get(httpUrl.host());
}
public void clearCookies() {
sCookieStore.clear();
}
}
@@ -0,0 +1,189 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http;
/**
* @author David González Verdugo
*/
public class HttpConstants {
/***********************************************************************************************************
*************************************************** HEADERS ***********************************************
***********************************************************************************************************/
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String COOKIE_HEADER = "Cookie";
public static final String BEARER_AUTHORIZATION_KEY = "Bearer ";
public static final String USER_AGENT_HEADER = "User-Agent";
public static final String IF_MATCH_HEADER = "If-Match";
public static final String IF_NONE_MATCH_HEADER = "If-None-Match";
public static final String CONTENT_TYPE_HEADER = "Content-Type";
public static final String CONTENT_LENGTH_HEADER = "Content-Length";
public static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length";
public static final String OC_X_OC_MTIME_HEADER = "X-OC-Mtime";
public static final String PARAM_SINGLE_COOKIE_HEADER = "http.protocol.single-cookie-header";
public static final String OC_X_REQUEST_ID = "X-Request-ID";
public static final String LOCATION_HEADER = "Location";
public static final String LOCATION_HEADER_LOWER = "location";
public static final String CONTENT_TYPE_URLENCODED_UTF8 = "application/x-www-form-urlencoded; charset=utf-8";
public static final String ACCEPT_ENCODING_HEADER = "Accept-Encoding";
public static final String ACCEPT_ENCODING_IDENTITY = "identity";
/***********************************************************************************************************
************************************************ STATUS CODES *********************************************
***********************************************************************************************************/
/**
* 1xx Informational
*/
// 100 Continue (HTTP/1.1 - RFC 2616)
public static final int HTTP_CONTINUE = 100;
// 101 Switching Protocols (HTTP/1.1 - RFC 2616)
public static final int HTTP_SWITCHING_PROTOCOLS = 101;
// 102 Processing (WebDAV - RFC 2518)
public static final int HTTP_PROCESSING = 102;
/**
* 2xx Success
*/
// 200 OK (HTTP/1.0 - RFC 1945)
public static final int HTTP_OK = 200;
// 201 Created (HTTP/1.0 - RFC 1945)
public static final int HTTP_CREATED = 201;
// 202 Accepted (HTTP/1.0 - RFC 1945)
public static final int HTTP_ACCEPTED = 202;
// 203 Non Authoritative Information (HTTP/1.1 - RFC 2616)
public static final int HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
// 204 No Content</tt> (HTTP/1.0 - RFC 1945)
public static final int HTTP_NO_CONTENT = 204;
// 205 Reset Content</tt> (HTTP/1.1 - RFC 2616)
public static final int HTTP_RESET_CONTENT = 205;
// 206 Partial Content</tt> (HTTP/1.1 - RFC 2616)
public static final int HTTP_PARTIAL_CONTENT = 206;
//207 Multi-Status (WebDAV - RFC 2518) or 207 Partial Update OK (HTTP/1.1 - draft-ietf-http-v11-spec-rev-01?)
public static final int HTTP_MULTI_STATUS = 207;
/**
* 3xx Redirection
*/
// 300 Mutliple Choices</tt> (HTTP/1.1 - RFC 2616)
public static final int HTTP_MULTIPLE_CHOICES = 300;
// 301 Moved Permanently</tt> (HTTP/1.0 - RFC 1945)
public static final int HTTP_MOVED_PERMANENTLY = 301;
// 302 Moved Temporarily</tt> (Sometimes <tt>Found) (HTTP/1.0 - RFC 1945)
public static final int HTTP_MOVED_TEMPORARILY = 302;
// 303 See Other (HTTP/1.1 - RFC 2616)
public static final int HTTP_SEE_OTHER = 303;
// 304 Not Modified (HTTP/1.0 - RFC 1945)
public static final int HTTP_NOT_MODIFIED = 304;
// 305 Use Proxy (HTTP/1.1 - RFC 2616)
public static final int HTTP_USE_PROXY = 305;
// 307 Temporary Redirect (HTTP/1.1 - RFC 2616)
public static final int HTTP_TEMPORARY_REDIRECT = 307;
/**
* 4xx Client Error
*/
// 400 Bad Request (HTTP/1.1 - RFC 2616)
public static final int HTTP_BAD_REQUEST = 400;
// 401 Unauthorized (HTTP/1.0 - RFC 1945)
public static final int HTTP_UNAUTHORIZED = 401;
// 402 Payment Required (HTTP/1.1 - RFC 2616)
public static final int HTTP_PAYMENT_REQUIRED = 402;
// 403 Forbidden (HTTP/1.0 - RFC 1945)
public static final int HTTP_FORBIDDEN = 403;
// 404 Not Found (HTTP/1.0 - RFC 1945)
public static final int HTTP_NOT_FOUND = 404;
// 405 Method Not Allowed (HTTP/1.1 - RFC 2616)
public static final int HTTP_METHOD_NOT_ALLOWED = 405;
// 406 Not Acceptable (HTTP/1.1 - RFC 2616)
public static final int HTTP_NOT_ACCEPTABLE = 406;
// 407 Proxy Authentication Required (HTTP/1.1 - RFC 2616)
public static final int HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
// 408 Request Timeout (HTTP/1.1 - RFC 2616)
public static final int HTTP_REQUEST_TIMEOUT = 408;
// 409 Conflict (HTTP/1.1 - RFC 2616)
public static final int HTTP_CONFLICT = 409;
// 410 Gone (HTTP/1.1 - RFC 2616)
public static final int HTTP_GONE = 410;
// 411 Length Required (HTTP/1.1 - RFC 2616)
public static final int HTTP_LENGTH_REQUIRED = 411;
// 412 Precondition Failed (HTTP/1.1 - RFC 2616)
public static final int HTTP_PRECONDITION_FAILED = 412;
// 413 Request Entity Too Large (HTTP/1.1 - RFC 2616)
public static final int HTTP_REQUEST_TOO_LONG = 413;
// 414 Request-URI Too Long (HTTP/1.1 - RFC 2616)
public static final int HTTP_REQUEST_URI_TOO_LONG = 414;
// 415 Unsupported Media Type (HTTP/1.1 - RFC 2616)
public static final int HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
// 416 Requested Range Not Satisfiable (HTTP/1.1 - RFC 2616)
public static final int HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
// 417 Expectation Failed (HTTP/1.1 - RFC 2616)
public static final int HTTP_EXPECTATION_FAILED = 417;
// 419 Insufficient Space on Resource (WebDAV - draft-ietf-webdav-protocol-05?)
// or <tt>419 Proxy Reauthentication Required (HTTP/1.1 drafts?)
public static final int HTTP_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
// 420 Method Failure (WebDAV - draft-ietf-webdav-protocol-05?)
public static final int HTTP_METHOD_FAILURE = 420;
// 422 Unprocessable Entity (WebDAV - RFC 2518)
public static final int HTTP_UNPROCESSABLE_ENTITY = 422;
// 423 Locked (WebDAV - RFC 2518)
public static final int HTTP_LOCKED = 423;
// 424 Failed Dependency (WebDAV - RFC 2518)
public static final int HTTP_FAILED_DEPENDENCY = 424;
/**
* 5xx Client Error
*/
// 500 Server Error (HTTP/1.0 - RFC 1945)
public static final int HTTP_INTERNAL_SERVER_ERROR = 500;
// 501 Not Implemented (HTTP/1.0 - RFC 1945)
public static final int HTTP_NOT_IMPLEMENTED = 501;
// 502 Bad Gateway (HTTP/1.0 - RFC 1945)
public static final int HTTP_BAD_GATEWAY = 502;
// 503 Service Unavailable (HTTP/1.0 - RFC 1945)
public static final int HTTP_SERVICE_UNAVAILABLE = 503;
// 504 Gateway Timeout (HTTP/1.1 - RFC 2616)
public static final int HTTP_GATEWAY_TIMEOUT = 504;
// 505 HTTP Version Not Supported (HTTP/1.1 - RFC 2616)
public static final int HTTP_HTTP_VERSION_NOT_SUPPORTED = 505;
// 507 Insufficient Storage (WebDAV - RFC 2518)
public static final int HTTP_INSUFFICIENT_STORAGE = 507;
/***********************************************************************************************************
*************************************************** TIMEOUTS **********************************************
***********************************************************************************************************/
/** Default timeout for waiting data from the server */
public static final int DEFAULT_DATA_TIMEOUT = 60000;
/** Default timeout for establishing a connection */
public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
}
@@ -0,0 +1,109 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* 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();
for (RequestInterceptor interceptor : mRequestInterceptors) {
request = interceptor.intercept(request);
}
Response response = chain.proceed(request);
for (ResponseInterceptor interceptor : mResponseInterceptors) {
response = interceptor.intercept(response);
}
return response;
}
public interface RequestInterceptor {
Request intercept(Request request) throws IOException;
}
public interface ResponseInterceptor {
Response intercept(Response response) throws IOException;
}
public HttpInterceptor addRequestInterceptor(RequestInterceptor requestInterceptor) {
mRequestInterceptors.add(requestInterceptor);
return this;
}
public HttpInterceptor addResponseInterceptor (ResponseInterceptor responseInterceptor) {
mResponseInterceptors.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) {
Iterator<RequestInterceptor> requestInterceptorIterator = mRequestInterceptors.iterator();
while (requestInterceptorIterator.hasNext()) {
RequestInterceptor currentRequestInterceptor = requestInterceptorIterator.next();
if (currentRequestInterceptor instanceof RequestHeaderInterceptor &&
((RequestHeaderInterceptor) currentRequestInterceptor).getHeaderName().equals(headerName)) {
requestInterceptorIterator.remove();
}
}
}
public ArrayList<ResponseInterceptor> getResponseInterceptors() {
return mResponseInterceptors;
}
}
@@ -0,0 +1,54 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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;
}
}
@@ -0,0 +1,192 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* 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 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();
}
public void setRetryOnConnectionFailure(boolean retryOnConnectionFailure) {
mOkHttpClient = mOkHttpClient.newBuilder()
.retryOnConnectionFailure(retryOnConnectionFailure)
.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();
}
}
@@ -0,0 +1,50 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.io.IOException;
import java.net.URL;
import okhttp3.HttpUrl;
/**
* 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();
}
}
@@ -0,0 +1,50 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.io.IOException;
import java.net.URL;
import okhttp3.HttpUrl;
/**
* 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();
}
}
@@ -0,0 +1,52 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import java.io.IOException;
import java.net.URL;
import okhttp3.Call;
import okhttp3.HttpUrl;
/**
* Wrapper to perform OkHttp calls
*
* @author David González Verdugo
*/
public abstract class HttpMethod extends HttpBaseMethod {
public HttpMethod(URL url) {
super(url);
}
@Override
public int onExecute() throws IOException {
mCall = mOkHttpClient.newCall(mRequest);
mResponse = mCall.execute();
return super.getStatusCode();
}
}
@@ -0,0 +1,50 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.io.IOException;
import java.net.URL;
import okhttp3.HttpUrl;
/**
* 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();
}
}
@@ -0,0 +1,46 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.io.IOException;
import java.net.URL;
import okhttp3.HttpUrl;
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();
}
}
@@ -0,0 +1,56 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.net.URL;
import kotlin.Unit;
/**
* 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();
}
}
@@ -0,0 +1,33 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.http.methods.webdav;
/**
* @author David González Verdugo
*/
public class DavConstants {
public static final int DEPTH_0 = 0;
public static final int DEPTH_1 = 1;
}
@@ -0,0 +1,158 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import at.bitfire.dav4android.Constants;
import at.bitfire.dav4android.DavOCResource;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4android.exception.RedirectException;
import okhttp3.HttpUrl;
import okhttp3.Protocol;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* 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) {
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 setRetryOnConnectionFailure(boolean retryOnConnectionFailure) {
super.setRetryOnConnectionFailure(retryOnConnectionFailure);
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());
}
//////////////////////////////
// Getter
//////////////////////////////
@Override
public boolean getRetryOnConnectionFailure() {
return false; //TODO: implement me
}
@Override
public boolean isAborted() {
return mDavResource.isCallAborted();
}
}
@@ -0,0 +1,15 @@
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();
}
}
@@ -0,0 +1,50 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.net.URL;
import kotlin.Unit;
/**
* MkCol calls wrapper
* @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();
}
}
@@ -0,0 +1,61 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.net.URL;
import kotlin.Unit;
/**
* 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();
}
}
@@ -0,0 +1,93 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import at.bitfire.dav4android.Property;
import at.bitfire.dav4android.Response;
import at.bitfire.dav4android.exception.DavException;
import kotlin.Unit;
/**
* 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;
}
}
@@ -0,0 +1,59 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.io.IOException;
import java.net.URL;
import at.bitfire.dav4android.exception.HttpException;
import kotlin.Unit;
/**
* 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();
}
}
@@ -0,0 +1,155 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import 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;
import java.security.cert.X509Certificate;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import com.owncloud.android.lib.common.utils.Log_OC;
/**
* @author David A. Velasco
*/
public class AdvancedX509TrustManager implements X509TrustManager {
private static final String TAG = AdvancedX509TrustManager.class.getSimpleName();
private X509TrustManager mStandardTrustManager = null;
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 {
super();
TrustManagerFactory factory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
factory.init((KeyStore)null);
mStandardTrustManager = findX509TrustManager(factory);
mKnownServersKeyStore = knownServersKeyStore;
}
/**
* Locates the first X509TrustManager provided by a given TrustManagerFactory
* @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 {
TrustManager tms[] = factory.getTrustManagers();
for (int i = 0; i < tms.length; i++) {
if (tms[i] instanceof X509TrustManager) {
return (X509TrustManager) tms[i];
}
}
return null;
}
/**
* @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],
* String authType)
*/
public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
mStandardTrustManager.checkClientTrusted(certificates, authType);
}
/**
* @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],
* String authType)
*/
public void checkServerTrusted(X509Certificate[] certificates, String authType) {
if (!isKnownServer(certificates[0])) {
CertificateCombinedException result = new CertificateCombinedException(certificates[0]);
try {
certificates[0].checkValidity();
} catch (CertificateExpiredException c) {
result.setCertificateExpiredException(c);
} catch (CertificateNotYetValidException c) {
result.setCertificateNotYetException(c);
}
try {
mStandardTrustManager.checkServerTrusted(certificates, authType);
} catch (CertificateException c) {
Throwable cause = c.getCause();
Throwable previousCause = null;
while (cause != null && cause != previousCause && !(cause instanceof CertPathValidatorException)) { // getCause() is not funny
previousCause = cause;
cause = cause.getCause();
}
if (cause != null && cause instanceof CertPathValidatorException) {
result.setCertPathValidatorException((CertPathValidatorException)cause);
} else {
result.setOtherCertificateException(c);
}
}
if (result.isException())
throw result;
}
}
/**
* @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
*/
public X509Certificate[] getAcceptedIssuers() {
return mStandardTrustManager.getAcceptedIssuers();
}
public boolean isKnownServer(X509Certificate cert) {
try {
return (mKnownServersKeyStore.getCertificateAlias(cert) != null);
} catch (KeyStoreException e) {
Log_OC.d(TAG, "Fail while checking certificate in the known-servers store");
return false;
}
}
}
@@ -0,0 +1,137 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLPeerUnverifiedException;
/**
* Exception joining all the problems that {@link AdvancedX509TrustManager} can find in
* a certificate chain for a server.
*
* This was initially created as an extension of CertificateException, but some
* implementations of the SSL socket layer in existing devices are REPLACING the CertificateException
* instances thrown by {@link javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], String)}
* with SSLPeerUnverifiedException FORGETTING THE CAUSING EXCEPTION instead of wrapping it.
*
* Due to this, extending RuntimeException is necessary to get that the CertificateCombinedException
* instance reaches {@link AdvancedSslSocketFactory#verifyPeerIdentity}.
*
* BE CAREFUL. As a RuntimeException extensions, Java compilers do not require to handle it
* in client methods. Be sure to use it only when you know exactly where it will go.
*
* @author David A. Velasco
*/
public class CertificateCombinedException extends RuntimeException {
/** Generated - to refresh every time the class changes */
private static final long serialVersionUID = -8875782030758554999L;
private X509Certificate mServerCert = null;
private String mHostInUrl;
private CertificateExpiredException mCertificateExpiredException = null;
private CertificateNotYetValidException mCertificateNotYetValidException = null;
private CertPathValidatorException mCertPathValidatorException = null;
private CertificateException mOtherCertificateException = null;
private SSLPeerUnverifiedException mSslPeerUnverifiedException = null;
public CertificateCombinedException(X509Certificate x509Certificate) {
mServerCert = x509Certificate;
}
public X509Certificate getServerCertificate() {
return mServerCert;
}
public String getHostInUrl() {
return mHostInUrl;
}
public void setHostInUrl(String host) {
mHostInUrl = host;
}
public CertificateExpiredException getCertificateExpiredException() {
return mCertificateExpiredException;
}
public void setCertificateExpiredException(CertificateExpiredException c) {
mCertificateExpiredException = c;
}
public CertificateNotYetValidException getCertificateNotYetValidException() {
return mCertificateNotYetValidException;
}
public void setCertificateNotYetException(CertificateNotYetValidException c) {
mCertificateNotYetValidException = c;
}
public CertPathValidatorException getCertPathValidatorException() {
return mCertPathValidatorException;
}
public void setCertPathValidatorException(CertPathValidatorException c) {
mCertPathValidatorException = c;
}
public CertificateException getOtherCertificateException() {
return mOtherCertificateException;
}
public void setOtherCertificateException(CertificateException c) {
mOtherCertificateException = c;
}
public SSLPeerUnverifiedException getSslPeerUnverifiedException() {
return mSslPeerUnverifiedException ;
}
public void setSslPeerUnverifiedException(SSLPeerUnverifiedException s) {
mSslPeerUnverifiedException = s;
}
public boolean isException() {
return (mCertificateExpiredException != null ||
mCertificateNotYetValidException != null ||
mCertPathValidatorException != null ||
mOtherCertificateException != null ||
mSslPeerUnverifiedException != null);
}
public boolean isRecoverable() {
return (mCertificateExpiredException != null ||
mCertificateNotYetValidException != null ||
mCertPathValidatorException != null ||
mSslPeerUnverifiedException != null);
}
}
@@ -0,0 +1,135 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import android.util.Log;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Iterator;
import okhttp3.MediaType;
import okio.BufferedSink;
/**
* A Request body that represents a file chunk and include information about the progress when uploading it
*
* @author David González Verdugo
*/
public class ChunkFromFileRequestBody extends FileRequestBody {
private static final String TAG = ChunkFromFileRequestBody.class.getSimpleName();
//private final File mFile;
private final FileChannel mChannel;
private final long mChunkSize;
private long mOffset;
private long mTransferred;
private ByteBuffer mBuffer = ByteBuffer.allocate(4096);
public ChunkFromFileRequestBody(File file, MediaType contentType, FileChannel channel, long chunkSize) {
super(file, contentType);
if (channel == null) {
throw new IllegalArgumentException("File may not be null");
}
if (chunkSize <= 0) {
throw new IllegalArgumentException("Chunk size must be greater than zero");
}
this.mChannel = channel;
this.mChunkSize = chunkSize;
mOffset = 0;
mTransferred = 0;
}
@Override
public long contentLength() {
try {
return Math.min(mChunkSize, mChannel.size() - mChannel.position());
} catch (IOException e) {
return mChunkSize;
}
}
@Override
public void writeTo(BufferedSink sink) {
int readCount;
Iterator<OnDatatransferProgressListener> it;
try {
mChannel.position(mOffset);
long size = mFile.length();
if (size == 0) size = -1;
long maxCount = Math.min(mOffset + mChunkSize, mChannel.size());
while (mChannel.position() < maxCount) {
Log_OC.d(TAG, "Sink buffer size: " + sink.buffer().size());
readCount = mChannel.read(mBuffer);
Log_OC.d(TAG, "Read " + readCount + " bytes from file channel to " + mBuffer.toString());
sink.buffer().write(mBuffer.array(), 0 ,readCount);
sink.flush();
Log_OC.d(TAG, "Write " + readCount + " bytes to sink buffer with size " + sink.buffer().size());
mBuffer.clear();
if (mTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks
mTransferred += readCount;
}
synchronized (mDataTransferListeners) {
it = mDataTransferListeners.iterator();
while (it.hasNext()) {
it.next().onTransferProgress(readCount, mTransferred, size, mFile.getAbsolutePath());
}
}
}
Log.d(TAG, "Chunk with size " + mChunkSize + " written in request body");
} catch (Exception exception) {
Log.e(TAG, exception.toString());
// // any read problem will be handled as if the file is not there
// if (io instanceof FileNotFoundException) {
// throw io;
// } else {
// FileNotFoundException fnf = new FileNotFoundException("Exception reading source file");
// fnf.initCause(io);
// throw fnf;
// }
}
}
public void setOffset(long offset) {
this.mOffset = offset;
}
}
@@ -0,0 +1,120 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import android.util.Log;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;
/**
* A Request body that represents a file and include information about the progress when uploading it
*
* @author David González Verdugo
*/
public class FileRequestBody extends RequestBody implements ProgressiveDataTransferer {
private static final String TAG = FileRequestBody.class.getSimpleName();
protected File mFile;
private MediaType mContentType;
Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
public FileRequestBody(File file, MediaType contentType) {
mFile = file;
mContentType = contentType;
}
@Override
public MediaType contentType() {
return mContentType;
}
@Override
public long contentLength() {
return mFile.length();
}
@Override
public void writeTo(BufferedSink sink) {
Source source;
Iterator<OnDatatransferProgressListener> it;
try {
source = Okio.source(mFile);
long transferred = 0;
long read;
while ((read = source.read(sink.buffer(), 4096)) != -1) {
transferred += read;
sink.flush();
synchronized (mDataTransferListeners) {
it = mDataTransferListeners.iterator();
while (it.hasNext()) {
it.next().onTransferProgress(read, transferred, mFile.length(), mFile.getAbsolutePath());
}
}
}
Log.d(TAG, "File with name " + mFile.getName() + " and size " + mFile.length() +
" written in request body");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
synchronized (mDataTransferListeners) {
mDataTransferListeners.add(listener);
}
}
@Override
public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners) {
synchronized (mDataTransferListeners) {
mDataTransferListeners.addAll(listeners);
}
}
@Override
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
synchronized (mDataTransferListeners) {
mDataTransferListeners.remove(listener);
}
}
}
@@ -0,0 +1,130 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import android.content.Context;
import com.owncloud.android.lib.common.utils.Log_OC;
public class NetworkUtils {
final private static String TAG = NetworkUtils.class.getSimpleName();
/** Default timeout for waiting data from the server */
public static final int DEFAULT_DATA_TIMEOUT = 60000;
/** Default timeout for establishing a connection */
public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
/** Standard name for protocol TLS version 1.2 in Java Secure Socket Extension (JSSE) API */
public static final String PROTOCOL_TLSv1_2 = "TLSv1.2";
/** Standard name for protocol TLS version 1.0 in JSSE API */
public static final String PROTOCOL_TLSv1_0 = "TLSv1";
private static X509HostnameVerifier mHostnameVerifier = null;
private static String LOCAL_TRUSTSTORE_FILENAME = "knownServers.bks";
private static String LOCAL_TRUSTSTORE_PASSWORD = "password";
private static KeyStore mKnownServersStore = null;
/**
* Returns the local store of reliable server certificates, explicitly accepted by the user.
*
* Returns a KeyStore instance with empty content if the local store was never created.
*
* Loads the store from the storage environment if needed.
*
* @param context Android context where the operation is being performed.
* @return KeyStore instance with explicitly-accepted server certificates.
* @throws KeyStoreException When the KeyStore instance could not be created.
* @throws IOException When an existing local trust store could not be loaded.
* @throws NoSuchAlgorithmException When the existing local trust store was saved with an unsupported algorithm.
* @throws CertificateException When an exception occurred while loading the certificates from the local
* trust store.
*/
public static KeyStore getKnownServersStore(Context context)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
if (mKnownServersStore == null) {
//mKnownServersStore = KeyStore.getInstance("BKS");
mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType());
File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME);
Log_OC.d(TAG, "Searching known-servers store at " + localTrustStoreFile.getAbsolutePath());
if (localTrustStoreFile.exists()) {
InputStream in = new FileInputStream(localTrustStoreFile);
try {
mKnownServersStore.load(in, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
} finally {
in.close();
}
} else {
// next is necessary to initialize an empty KeyStore instance
mKnownServersStore.load(null, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
}
}
return mKnownServersStore;
}
public static void addCertToKnownServersStore(Certificate cert, Context context)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
KeyStore knownServers = getKnownServersStore(context);
knownServers.setCertificateEntry(Integer.toString(cert.hashCode()), cert);
FileOutputStream fos = null;
try {
fos = context.openFileOutput(LOCAL_TRUSTSTORE_FILENAME, Context.MODE_PRIVATE);
knownServers.store(fos, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
} finally {
fos.close();
}
}
public static boolean isCertInKnownServersStore(Certificate cert, Context context)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
KeyStore knownServers = getKnownServersStore(context);
Log_OC.d(TAG, "Certificate - HashCode: " + cert.hashCode() + " "
+ Boolean.toString(knownServers.isCertificateEntry(Integer.toString(cert.hashCode()))));
return knownServers.isCertificateEntry(Integer.toString(cert.hashCode()));
}
}
@@ -0,0 +1,30 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
* Copyright (C) 2012 Bartek Przybylski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
public interface OnDatatransferProgressListener {
void onTransferProgress(long read, long transferred, long percent, String absolutePath);
}
@@ -0,0 +1,38 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import java.util.Collection;
public interface ProgressiveDataTransferer {
public void addDatatransferProgressListener (OnDatatransferProgressListener listener);
public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners);
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener);
}
@@ -0,0 +1,128 @@
/* ownCloud Android Library is available under MIT license
*
* @author David A. Velasco
*
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import com.owncloud.android.lib.common.http.HttpConstants;
import java.util.Arrays;
/**
* Aggregate saving the list of URLs followed in a sequence of redirections during the exceution of a
* {@link RemoteOperation}, and the status codes corresponding to all
* of them.
*
* The last status code saved corresponds to the first response not being a redirection, unless the sequence exceeds
* the maximum length of redirections allowed by the {@link com.owncloud.android.lib.common.OwnCloudClient} instance
* that ran the operation.
*
* If no redirection was followed, the last (and first) status code contained corresponds to the original URL in the
* request.
*/
public class RedirectionPath {
private int[] mStatuses = null;
private int mLastStatus = -1;
private String[] mLocations = null;
private int mLastLocation = -1;
/**
* Public constructor.
*
* @param status Status code resulting of executing a request on the original URL.
* @param maxRedirections Maximum number of redirections that will be contained.
* @throws IllegalArgumentException If 'maxRedirections' is < 0
*/
public RedirectionPath(int status, int maxRedirections) {
if (maxRedirections < 0) {
throw new IllegalArgumentException("maxRedirections MUST BE zero or greater");
}
mStatuses = new int[maxRedirections + 1];
Arrays.fill(mStatuses, -1);
mStatuses[++mLastStatus] = status;
}
/**
* Adds a new location URL to the list of followed redirections.
*
* @param location URL extracted from a 'Location' header in a redirection.
*/
public void addLocation(String location) {
if (mLocations == null) {
mLocations = new String[mStatuses.length - 1];
}
if (mLastLocation < mLocations.length - 1) {
mLocations[++mLastLocation] = location;
}
}
/**
* Adds a new status code to the list of status corresponding to followed redirections.
*
* @param status Status code from the response of another followed redirection.
*/
public void addStatus(int status) {
if (mLastStatus < mStatuses.length - 1) {
mStatuses[++mLastStatus] = status;
}
}
/**
* @return Last status code saved.
*/
public int getLastStatus() {
return mStatuses[mLastStatus];
}
/**
* @return Last location followed corresponding to a permanent redirection (status code 301).
*/
public String getLastPermanentLocation() {
for (int i = mLastStatus; i >= 0; i--) {
if (mStatuses[i] == HttpConstants.HTTP_MOVED_PERMANENTLY && i <= mLastLocation) {
return mLocations[i];
}
}
return null;
}
/**
* @return Count of locations.
*/
public int getRedirectionsCount() {
return mLastLocation + 1;
}
}
@@ -0,0 +1,151 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLSocket;
import com.owncloud.android.lib.common.utils.Log_OC;
/**
* Enables the support of Server Name Indication if existing
* in the underlying network implementation.
*
* Build as a singleton.
*
* @author David A. Velasco
*/
public class ServerNameIndicator {
private static final String TAG = ServerNameIndicator.class.getSimpleName();
private static final AtomicReference<ServerNameIndicator> mSingleInstance = new AtomicReference<ServerNameIndicator>();
private static final String METHOD_NAME = "setHostname";
private final WeakReference<Class<?>> mSSLSocketClassRef;
private final WeakReference<Method> mSetHostnameMethodRef;
/**
* Private constructor, class is a singleton.
*
* @param sslSocketClass Underlying implementation class of {@link SSLSocket} used to connect with the server.
* @param setHostnameMethod Name of the method to call to enable the SNI support.
*/
private ServerNameIndicator(Class<?> sslSocketClass, Method setHostnameMethod) {
mSSLSocketClassRef = new WeakReference<Class<?>>(sslSocketClass);
mSetHostnameMethodRef = (setHostnameMethod == null) ? null : new WeakReference<Method>(setHostnameMethod);
}
/**
* Calls the {@code #setHostname(String)} method of the underlying implementation
* of {@link SSLSocket} if exists.
*
* Creates and initializes the single instance of the class when needed
*
* @param hostname The name of the server host of interest.
* @param sslSocket Client socket to connect with the server.
*/
public static void setServerNameIndication(String hostname, SSLSocket sslSocket) {
final Method setHostnameMethod = getMethod(sslSocket);
if (setHostnameMethod != null) {
try {
setHostnameMethod.invoke(sslSocket, hostname);
Log_OC.i(TAG, "SNI done, hostname: " + hostname);
} catch (IllegalArgumentException e) {
Log_OC.e(TAG, "Call to SSLSocket#setHost(String) failed ", e);
} catch (IllegalAccessException e) {
Log_OC.e(TAG, "Call to SSLSocket#setHost(String) failed ", e);
} catch (InvocationTargetException e) {
Log_OC.e(TAG, "Call to SSLSocket#setHost(String) failed ", e);
}
} else {
Log_OC.i(TAG, "SNI not supported");
}
}
/**
* Gets the method to invoke trying to minimize the effective
* application of reflection.
*
* @param sslSocket Instance of the SSL socket to use in connection with server.
* @return Method to call to indicate the server name of interest to the server.
*/
private static Method getMethod(SSLSocket sslSocket) {
final Class<?> sslSocketClass = sslSocket.getClass();
final ServerNameIndicator instance = mSingleInstance.get();
if (instance == null) {
return initFrom(sslSocketClass);
} else if (instance.mSSLSocketClassRef.get() != sslSocketClass) {
// the underlying class changed
return initFrom(sslSocketClass);
} else if (instance.mSetHostnameMethodRef == null) {
// SNI not supported
return null;
} else {
final Method cachedSetHostnameMethod = instance.mSetHostnameMethodRef.get();
return (cachedSetHostnameMethod == null) ? initFrom(sslSocketClass) : cachedSetHostnameMethod;
}
}
/**
* Singleton initializer.
*
* Uses reflection to extract and 'cache' the method to invoke to indicate the desited host name to the server side.
*
* @param sslSocketClass Underlying class providing the implementation of {@link SSLSocket}.
* @return Method to call to indicate the server name of interest to the server.
*/
private static Method initFrom(Class<?> sslSocketClass) {
Log_OC.i(TAG, "SSLSocket implementation: " + sslSocketClass.getCanonicalName());
Method setHostnameMethod = null;
try {
setHostnameMethod = sslSocketClass.getMethod(METHOD_NAME, String.class);
} catch (SecurityException e) {
Log_OC.e(TAG, "Could not access to SSLSocket#setHostname(String) method ", e);
} catch (NoSuchMethodException e) {
Log_OC.i(TAG, "Could not find SSLSocket#setHostname(String) method - SNI not supported");
}
mSingleInstance.set(new ServerNameIndicator(sslSocketClass, setHostnameMethod));
return setHostnameMethod;
}
}
@@ -0,0 +1,124 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
* Copyright (C) 2012 Bartek Przybylski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import android.net.Uri;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
public class WebdavUtils {
public static final SimpleDateFormat DISPLAY_DATE_FORMAT = new SimpleDateFormat(
"dd.MM.yyyy hh:mm");
private static final SimpleDateFormat DATETIME_FORMATS[] = {
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US),
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'", Locale.US),
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US),
new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US),
new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US),
new SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.US)
};
public static Date parseResponseDate(String date) {
Date returnDate = null;
SimpleDateFormat format = null;
for (int i = 0; i < DATETIME_FORMATS.length; ++i) {
try {
format = DATETIME_FORMATS[i];
synchronized(format) {
returnDate = format.parse(date);
}
return returnDate;
} catch (ParseException e) {
// this is not the format
}
}
return null;
}
/**
* Encodes a path according to URI RFC 2396.
*
* If the received path doesn't start with "/", the method adds it.
*
* @param remoteFilePath Path
* @return Encoded path according to RFC 2396, always starting with "/"
*/
public static String encodePath(String remoteFilePath) {
String encodedPath = Uri.encode(remoteFilePath, "/");
if (!encodedPath.startsWith("/"))
encodedPath = "/" + encodedPath;
return encodedPath;
}
/**
*
* @param rawEtag
* @return
*/
public static String parseEtag(String rawEtag) {
if (rawEtag == null || rawEtag.length() == 0) {
return "";
}
if (rawEtag.endsWith("-gzip")) {
rawEtag = rawEtag.substring(0, rawEtag.length() - 5);
}
if (rawEtag.length() >= 2 && rawEtag.startsWith("\"") && rawEtag.endsWith("\"")) {
rawEtag = rawEtag.substring(1, rawEtag.length() - 1);
}
return rawEtag;
}
/**
*
* @param httpBaseMethod from which to get the etag
* @return etag from response
*/
public static String getEtagFromResponse(HttpBaseMethod httpBaseMethod) {
String eTag = httpBaseMethod.getResponseHeader("OC-ETag");
if (eTag == null) {
eTag = httpBaseMethod.getResponseHeader("oc-etag");
}
if (eTag == null) {
eTag = httpBaseMethod.getResponseHeader("ETag");
}
if (eTag == null) {
eTag = httpBaseMethod.getResponseHeader("etag");
}
String result = "";
if (eTag != null) {
result = eTag;
}
return result;
}
}
@@ -0,0 +1,173 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.concurrent.atomic.AtomicReference;
/**
* Enforces, if possible, a write timeout for a socket.
*
* Built as a singleton.
*
* Tries to hit something like this:
* https://android.googlesource.com/platform/external/conscrypt/+/lollipop-release/src/main/java/org/conscrypt/OpenSSLSocketImpl.java#1005
*
* Minimizes the chances of getting stalled in PUT/POST request if the network interface is lost while
* writing the entity into the outwards sockect.
*
* It happens. See https://github.com/owncloud/android/issues/1684#issuecomment-295306015
*
* @author David A. Velasco
*/
public class WriteTimeoutEnforcer {
private static final String TAG = WriteTimeoutEnforcer.class.getSimpleName();
private static final AtomicReference<WriteTimeoutEnforcer> mSingleInstance = new AtomicReference<>();
private static final String METHOD_NAME = "setSoWriteTimeout";
private final WeakReference<Class<?>> mSocketClassRef;
private final WeakReference<Method> mSetSoWriteTimeoutMethodRef;
/**
* Private constructor, class is a singleton.
*
* @param socketClass Underlying implementation class of {@link Socket} used to connect
* with the server.
* @param setSoWriteTimeoutMethod Name of the method to call to set a write timeout in the socket.
*/
private WriteTimeoutEnforcer(Class<?> socketClass, Method setSoWriteTimeoutMethod) {
mSocketClassRef = new WeakReference<Class<?>>(socketClass);
mSetSoWriteTimeoutMethodRef =
(setSoWriteTimeoutMethod == null) ?
null :
new WeakReference<>(setSoWriteTimeoutMethod)
;
}
/**
* Calls the {@code #setSoWrite(int)} method of the underlying implementation
* of {@link Socket} if exists.
* Creates and initializes the single instance of the class when needed
*
* @param writeTimeoutMilliseconds Write timeout to set, in milliseconds.
* @param socket Client socket to connect with the server.
*/
public static void setSoWriteTimeout(int writeTimeoutMilliseconds, Socket socket) {
final Method setSoWriteTimeoutMethod = getMethod(socket);
if (setSoWriteTimeoutMethod != null) {
try {
setSoWriteTimeoutMethod.invoke(socket, writeTimeoutMilliseconds);
Log_OC.i(
TAG,
"Write timeout set in socket, writeTimeoutMilliseconds: "
+ writeTimeoutMilliseconds
);
} catch (IllegalArgumentException e) {
Log_OC.e(TAG, "Call to (SocketImpl)#setSoWriteTimeout(int) failed ", e);
} catch (IllegalAccessException e) {
Log_OC.e(TAG, "Call to (SocketImpl)#setSoWriteTimeout(int) failed ", e);
} catch (InvocationTargetException e) {
Log_OC.e(TAG, "Call to (SocketImpl)#setSoWriteTimeout(int) failed ", e);
}
} else {
Log_OC.i(TAG, "Write timeout for socket not supported");
}
}
/**
* Gets the method to invoke trying to minimize the cost of reflection reusing objects cached
* in static members.
*
* @param socket Instance of the socket to use in connection with server.
* @return Method to call to set a write timeout in the socket.
*/
private static Method getMethod(Socket socket) {
final Class<?> socketClass = socket.getClass();
final WriteTimeoutEnforcer instance = mSingleInstance.get();
if (instance == null) {
return initFrom(socketClass);
} else if (instance.mSocketClassRef.get() != socketClass) {
// the underlying class changed
return initFrom(socketClass);
} else if (instance.mSetSoWriteTimeoutMethodRef == null) {
// method not supported
return null;
} else {
final Method cachedSetSoWriteTimeoutMethod = instance.mSetSoWriteTimeoutMethodRef.get();
return (cachedSetSoWriteTimeoutMethod == null) ?
initFrom(socketClass) :
cachedSetSoWriteTimeoutMethod
;
}
}
/**
* Singleton initializer.
*
* Uses reflection to extract and 'cache' the method to invoke to set a write timouet in a socket.
*
* @param socketClass Underlying class providing the implementation of {@link Socket}.
* @return Method to call to set a write timeout in the socket.
*/
private static Method initFrom(Class<?> socketClass) {
Log_OC.i(TAG, "Socket implementation: " + socketClass.getCanonicalName());
Method setSoWriteTimeoutMethod = null;
try {
setSoWriteTimeoutMethod = socketClass.getMethod(METHOD_NAME, int.class);
} catch (SecurityException e) {
Log_OC.e(TAG, "Could not access to (SocketImpl)#setSoWriteTimeout(int) method ", e);
} catch (NoSuchMethodException e) {
Log_OC.i(
TAG,
"Could not find (SocketImpl)#setSoWriteTimeout(int) method - write timeout not supported"
);
}
mSingleInstance.set(new WriteTimeoutEnforcer(socketClass, setSoWriteTimeoutMethod));
return setSoWriteTimeoutMethod;
}
}
@@ -0,0 +1,138 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.operations;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
/**
* Parser for server exceptions
* @author davidgonzalez
*/
public class ErrorMessageParser {
// No namespaces
private static final String ns = null;
// Nodes for XML Parser
private static final String NODE_ERROR = "d:error";
private static final String NODE_MESSAGE = "s:message";
/**
* Parse exception response
* @param is
* @return errorMessage for an exception
* @throws XmlPullParserException
* @throws IOException
*/
public String parseXMLResponse(InputStream is) throws XmlPullParserException,
IOException {
String errorMessage = "";
try {
// XMLPullParser
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(is, null);
parser.nextTag();
errorMessage = readError(parser);
} finally {
is.close();
}
return errorMessage;
}
/**
* Parse OCS node
* @param parser
* @return reason for exception
* @throws XmlPullParserException
* @throws IOException
*/
private String readError (XmlPullParser parser) throws XmlPullParserException, IOException {
String errorMessage = "";
parser.require(XmlPullParser.START_TAG, ns , NODE_ERROR);
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
// read NODE_MESSAGE
if (name.equalsIgnoreCase(NODE_MESSAGE)) {
errorMessage = readText(parser);
} else {
skip(parser);
}
}
return errorMessage;
}
/**
* Skip tags in parser procedure
* @param parser
* @throws XmlPullParserException
* @throws IOException
*/
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
if (parser.getEventType() != XmlPullParser.START_TAG) {
throw new IllegalStateException();
}
int depth = 1;
while (depth != 0) {
switch (parser.next()) {
case XmlPullParser.END_TAG:
depth--;
break;
case XmlPullParser.START_TAG:
depth++;
break;
}
}
}
/**
* Read the text from a node
* @param parser
* @return Text of the node
* @throws IOException
* @throws XmlPullParserException
*/
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
String result = "";
if (parser.next() == XmlPullParser.TEXT) {
result = parser.getText();
parser.nextTag();
}
return result;
}
}
@@ -0,0 +1,144 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.operations;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
/**
* Parser for Invalid Character server exception
* @author masensio
*/
public class InvalidCharacterExceptionParser {
private static final String EXCEPTION_STRING = "OC\\Connector\\Sabre\\Exception\\InvalidPath";
private static final String EXCEPTION_UPLOAD_STRING = "OCP\\Files\\InvalidPathException";
// No namespaces
private static final String ns = null;
// Nodes for XML Parser
private static final String NODE_ERROR = "d:error";
private static final String NODE_EXCEPTION = "s:exception";
/**
* Parse is as an Invalid Path Exception
* @param is
* @return if The exception is an Invalid Char Exception
* @throws XmlPullParserException
* @throws IOException
*/
public boolean parseXMLResponse(InputStream is) throws XmlPullParserException,
IOException {
boolean result = false;
try {
// XMLPullParser
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(is, null);
parser.nextTag();
result = readError(parser);
} finally {
is.close();
}
return result;
}
/**
* Parse OCS node
* @param parser
* @return List of ShareRemoteFiles
* @throws XmlPullParserException
* @throws IOException
*/
private boolean readError (XmlPullParser parser) throws XmlPullParserException, IOException {
String exception = "";
parser.require(XmlPullParser.START_TAG, ns , NODE_ERROR);
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
// read NODE_EXCEPTION
if (name.equalsIgnoreCase(NODE_EXCEPTION)) {
exception = readText(parser);
} else {
skip(parser);
}
}
return exception.equalsIgnoreCase(EXCEPTION_STRING) ||
exception.equalsIgnoreCase(EXCEPTION_UPLOAD_STRING);
}
/**
* Skip tags in parser procedure
* @param parser
* @throws XmlPullParserException
* @throws IOException
*/
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
if (parser.getEventType() != XmlPullParser.START_TAG) {
throw new IllegalStateException();
}
int depth = 1;
while (depth != 0) {
switch (parser.next()) {
case XmlPullParser.END_TAG:
depth--;
break;
case XmlPullParser.START_TAG:
depth++;
break;
}
}
}
/**
* Read the text from a node
* @param parser
* @return Text of the node
* @throws IOException
* @throws XmlPullParserException
*/
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
String result = "";
if (parser.next() == XmlPullParser.TEXT) {
result = parser.getText();
parser.nextTag();
}
return result;
}
}
@@ -0,0 +1,32 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.operations;
public interface OnRemoteOperationListener {
void onRemoteOperationFinish(RemoteOperation caller, RemoteOperationResult result);
}
@@ -0,0 +1,34 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.operations;
public class OperationCancelledException extends Exception {
/**
* Generated serial version - to avoid Java warning
*/
private static final long serialVersionUID = -6350981497740424983L;
}
@@ -0,0 +1,285 @@
package com.owncloud.android.lib.common.operations;
import android.accounts.Account;
import android.accounts.AccountsException;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.os.Handler;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.IOException;
import okhttp3.OkHttpClient;
public abstract class RemoteOperation<T extends Object> implements Runnable {
private static final String TAG = RemoteOperation.class.getSimpleName();
/**
* OCS API header name
*/
public static final String OCS_API_HEADER = "OCS-APIREQUEST";
/**
* OCS API header value
*/
public static final String OCS_API_HEADER_VALUE = "true";
/**
* ownCloud account in the remote ownCloud server to operate
*/
protected Account mAccount = null;
/**
* Android Application context
*/
protected Context mContext = null;
/**
* Object to interact with the remote server
*/
protected OwnCloudClient mClient = null;
/**
* Object to interact with the remote server
*/
protected OkHttpClient mHttpClient = null;
/**
* Callback object to notify about the execution of the remote operation
*/
protected OnRemoteOperationListener mListener = null;
/**
* Handler to the thread where mListener methods will be called
*/
protected Handler mListenerHandler = null;
/**
* Asynchronously executes the remote operation
*
* This method should be used whenever an ownCloud account is available,
* instead of {@link #execute(OwnCloudClient, OnRemoteOperationListener, Handler))}.
*
* @param account ownCloud account in remote ownCloud server to reach during the
* execution of the operation.
* @param context Android context for the component calling the method.
* @param listener Listener to be notified about the execution of the operation.
* @param listenerHandler Handler associated to the thread where the methods of the listener
* objects must be called.
* @return Thread were the remote operation is executed.
*/
public Thread execute(Account account, Context context,
OnRemoteOperationListener listener, Handler listenerHandler) {
if (account == null)
throw new IllegalArgumentException
("Trying to execute a remote operation with a NULL Account");
if (context == null)
throw new IllegalArgumentException
("Trying to execute a remote operation with a NULL Context");
// mAccount and mContext in the runnerThread to create below
mAccount = account;
mContext = context.getApplicationContext();
mClient = null; // the client instance will be created from
mListener = listener;
mListenerHandler = listenerHandler;
Thread runnerThread = new Thread(this);
runnerThread.start();
return runnerThread;
}
/**
* Asynchronously executes the remote operation
*
* @param client Client object to reach an ownCloud server
* during the execution of the operation.
* @param listener Listener to be notified about the execution of the operation.
* @param listenerHandler Handler, if passed in, associated to the thread where the methods of
* the listener objects must be called.
* @return Thread were the remote operation is executed.
*/
public Thread execute(OwnCloudClient client,
OnRemoteOperationListener listener, Handler listenerHandler) {
if (client == null) {
throw new IllegalArgumentException
("Trying to execute a remote operation with a NULL OwnCloudClient");
}
mClient = client;
if (client.getAccount() != null) {
mAccount = client.getAccount().getSavedAccount();
}
mContext = client.getContext();
if (listener == null) {
throw new IllegalArgumentException
("Trying to execute a remote operation asynchronously " +
"without a listener to notiy the result");
}
mListener = listener;
if (listenerHandler != null) {
mListenerHandler = listenerHandler;
}
Thread runnerThread = new Thread(this);
runnerThread.start();
return runnerThread;
}
protected void grantOwnCloudClient() throws
AccountUtils.AccountNotFoundException, OperationCanceledException, AuthenticatorException, IOException {
if (mClient == null) {
if (mAccount != null && mContext != null) {
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, mContext);
} else {
throw new IllegalStateException("Trying to run a remote operation " +
"asynchronously with no client and no chance to create one (no account)");
}
}
}
/**
* Returns the current client instance to access the remote server.
*
* @return Current client instance to access the remote server.
*/
public final OwnCloudClient getClient() {
return mClient;
}
/**
* Abstract method to implement the operation in derived classes.
*/
protected abstract RemoteOperationResult<T> run(OwnCloudClient client);
/**
* Synchronously executes the remote operation on the received ownCloud account.
*
* Do not call this method from the main thread.
*
* This method should be used whenever an ownCloud account is available, instead of
* {@link #execute(OwnCloudClient)}.
*
* @param account ownCloud account in remote ownCloud server to reach during the
* execution of the operation.
* @param context Android context for the component calling the method.
* @return Result of the operation.
*/
public RemoteOperationResult<T> execute(Account account, Context context) {
if (account == null)
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL " +
"Account");
if (context == null)
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL " +
"Context");
mAccount = account;
mContext = context.getApplicationContext();
return runOperation();
}
/**
* Synchronously executes the remote operation
*
* Do not call this method from the main thread.
*
* @param client Client object to reach an ownCloud server during the execution of
* the operation.
* @return Result of the operation.
*/
public RemoteOperationResult<T> execute(OwnCloudClient client) {
if (client == null)
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL " +
"OwnCloudClient");
mClient = client;
if (client.getAccount() != null) {
mAccount = client.getAccount().getSavedAccount();
}
mContext = client.getContext();
return runOperation();
}
/**
* Synchronously executes the remote operation
*
* Do not call this method from the main thread.
*
* @param client Client object to reach an ownCloud server during the execution of
* the operation.
* @return Result of the operation.
*/
public RemoteOperationResult<T> execute(OkHttpClient client, Context context) {
if (client == null)
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL " +
"OwnCloudClient");
mHttpClient = client;
mContext = context;
return runOperation();
}
/**
* Run operation for asynchronous or synchronous 'onExecute' method.
*
* Considers and performs silent refresh of account credentials if possible, and if
* {@link RemoteOperation#setSilentRefreshOfAccountCredentials(boolean)} was called with
* parameter 'true' before the execution.
*
* @return Remote operation result
*/
private RemoteOperationResult<T> runOperation() {
RemoteOperationResult<T> result;
try {
grantOwnCloudClient();
result = run(mClient);
} catch (AccountsException | IOException e) {
Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
result = new RemoteOperationResult<>(e);
}
return result;
}
/**
* Asynchronous execution of the operation
* started by {@link RemoteOperation#execute(OwnCloudClient,
* OnRemoteOperationListener, Handler)},
* and result posting.
*/
@Override
public final void run() {
final RemoteOperationResult resultToSend = runOperation();
if (mAccount != null && mContext != null) {
// Save Client Cookies
AccountUtils.saveClient(mClient, mAccount, mContext);
}
if (mListenerHandler != null && mListener != null) {
mListenerHandler.post(() ->
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend));
} else if (mListener != null) {
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
}
}
}
@@ -0,0 +1,586 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.operations;
import android.accounts.Account;
import android.accounts.AccountsException;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
import com.owncloud.android.lib.common.network.CertificateCombinedException;
import com.owncloud.android.lib.common.utils.Log_OC;
import org.json.JSONException;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
import okhttp3.Headers;
public class RemoteOperationResult<T extends Object>
implements Serializable {
/**
* Generated - should be refreshed every time the class changes!!
*/
private static final long serialVersionUID = 4968939884332372230L;
private static final String TAG = RemoteOperationResult.class.getSimpleName();
public enum ResultCode {
OK,
OK_SSL,
OK_NO_SSL,
UNHANDLED_HTTP_CODE,
UNAUTHORIZED,
FILE_NOT_FOUND,
INSTANCE_NOT_CONFIGURED,
UNKNOWN_ERROR,
WRONG_CONNECTION,
TIMEOUT,
INCORRECT_ADDRESS,
HOST_NOT_AVAILABLE,
NO_NETWORK_CONNECTION,
SSL_ERROR,
SSL_RECOVERABLE_PEER_UNVERIFIED,
BAD_OC_VERSION,
CANCELLED,
INVALID_LOCAL_FILE_NAME,
INVALID_OVERWRITE,
CONFLICT,
OAUTH2_ERROR,
SYNC_CONFLICT,
LOCAL_STORAGE_FULL,
LOCAL_STORAGE_NOT_MOVED,
LOCAL_STORAGE_NOT_COPIED,
OAUTH2_ERROR_ACCESS_DENIED,
QUOTA_EXCEEDED,
ACCOUNT_NOT_FOUND,
ACCOUNT_EXCEPTION,
ACCOUNT_NOT_NEW,
ACCOUNT_NOT_THE_SAME,
INVALID_CHARACTER_IN_NAME,
SHARE_NOT_FOUND,
LOCAL_STORAGE_NOT_REMOVED,
FORBIDDEN,
SHARE_FORBIDDEN,
SPECIFIC_FORBIDDEN,
OK_REDIRECT_TO_NON_SECURE_CONNECTION,
INVALID_MOVE_INTO_DESCENDANT,
INVALID_COPY_INTO_DESCENDANT,
PARTIAL_MOVE_DONE,
PARTIAL_COPY_DONE,
SHARE_WRONG_PARAMETER,
WRONG_SERVER_RESPONSE,
INVALID_CHARACTER_DETECT_IN_SERVER,
DELAYED_FOR_WIFI,
LOCAL_FILE_NOT_FOUND,
SERVICE_UNAVAILABLE,
SPECIFIC_SERVICE_UNAVAILABLE,
SPECIFIC_UNSUPPORTED_MEDIA_TYPE,
SPECIFIC_METHOD_NOT_ALLOWED
}
private boolean mSuccess = false;
private int mHttpCode = -1;
private String mHttpPhrase = null;
private Exception mException = null;
private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
private String mRedirectedLocation;
private ArrayList<String> mAuthenticate = new ArrayList<>();
private String mLastPermanentLocation = null;
private T mData = null;
/**
* Public constructor from result code.
*
* To be used when the caller takes the responsibility of interpreting the result of a {@link RemoteOperation}
*
* @param code {@link ResultCode} decided by the caller.
*/
public RemoteOperationResult(ResultCode code) {
mCode = code;
mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL ||
code == ResultCode.OK_NO_SSL ||
code == ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION);
}
/**
* Create a new RemoteOperationResult based on the result given by a previous one.
* It does not copy the data.
* @param prevRemoteOperation
*/
public RemoteOperationResult(RemoteOperationResult prevRemoteOperation) {
mCode = prevRemoteOperation.mCode;
mHttpCode = prevRemoteOperation.mHttpCode;
mHttpPhrase = prevRemoteOperation.mHttpPhrase;
mAuthenticate = prevRemoteOperation.mAuthenticate;
mException = prevRemoteOperation.mException;
mLastPermanentLocation = prevRemoteOperation.mLastPermanentLocation;
mSuccess = prevRemoteOperation.mSuccess;
mRedirectedLocation = prevRemoteOperation.mRedirectedLocation;
}
/**
* Public constructor from exception.
*
* To be used when an exception prevented the end of the {@link RemoteOperation}.
*
* Determines a {@link ResultCode} depending on the type of the exception.
*
* @param e Exception that interrupted the {@link RemoteOperation}
*/
public RemoteOperationResult(Exception e) {
mException = e;
if (e instanceof OperationCancelledException) {
mCode = ResultCode.CANCELLED;
} else if (e instanceof SocketException) {
mCode = ResultCode.WRONG_CONNECTION;
} else if (e instanceof SocketTimeoutException) {
mCode = ResultCode.TIMEOUT;
} else if (e instanceof MalformedURLException) {
mCode = ResultCode.INCORRECT_ADDRESS;
} else if (e instanceof UnknownHostException) {
mCode = ResultCode.HOST_NOT_AVAILABLE;
} else if (e instanceof AccountUtils.AccountNotFoundException) {
mCode = ResultCode.ACCOUNT_NOT_FOUND;
} else if (e instanceof AccountsException) {
mCode = ResultCode.ACCOUNT_EXCEPTION;
} else if (e instanceof SSLException || e instanceof RuntimeException) {
if(e instanceof SSLPeerUnverifiedException) {
mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
} else {
CertificateCombinedException se = getCertificateCombinedException(e);
if (se != null) {
mException = se;
if (se.isRecoverable()) {
mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
}
} else if (e instanceof RuntimeException) {
mCode = ResultCode.HOST_NOT_AVAILABLE;
} else {
mCode = ResultCode.SSL_ERROR;
}
}
} else if (e instanceof FileNotFoundException) {
mCode = ResultCode.LOCAL_FILE_NOT_FOUND;
} else {
mCode = ResultCode.UNKNOWN_ERROR;
}
}
/**
* Public constructor from separate elements of an HTTP or DAV response.
*
* To be used when the result needs to be interpreted from the response of an HTTP/DAV method.
*
* Determines a {@link ResultCode} from the already executed method received as a parameter. Generally,
* will depend on the HTTP code and HTTP response headers received. In some cases will inspect also the
* response body
*
* @param httpMethod
* @throws IOException
*/
public RemoteOperationResult(HttpBaseMethod httpMethod) throws IOException {
this(httpMethod.getStatusCode(),
httpMethod.getStatusMessage(),
httpMethod.getResponseHeaders()
);
if (mHttpCode == HttpConstants.HTTP_BAD_REQUEST) { // 400
String bodyResponse = httpMethod.getResponseBodyAsString();
// do not get for other HTTP codes!; could not be available
if (bodyResponse != null && bodyResponse.length() > 0) {
InputStream is = new ByteArrayInputStream(bodyResponse.getBytes());
InvalidCharacterExceptionParser xmlParser = new InvalidCharacterExceptionParser();
try {
if (xmlParser.parseXMLResponse(is)) {
mCode = ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER;
}
} catch (Exception e) {
Log_OC.w(TAG, "Error reading exception from server: " + e.getMessage());
// mCode stays as set in this(success, httpCode, headers)
}
}
}
// before
switch (mHttpCode) {
case HttpConstants.HTTP_FORBIDDEN:
parseErrorMessageAndSetCode(
httpMethod.getResponseBodyAsString(),
ResultCode.SPECIFIC_FORBIDDEN
);
break;
case HttpConstants.HTTP_UNSUPPORTED_MEDIA_TYPE:
parseErrorMessageAndSetCode(
httpMethod.getResponseBodyAsString(),
ResultCode.SPECIFIC_UNSUPPORTED_MEDIA_TYPE
);
break;
case HttpConstants.HTTP_SERVICE_UNAVAILABLE:
parseErrorMessageAndSetCode(
httpMethod.getResponseBodyAsString(),
ResultCode.SPECIFIC_SERVICE_UNAVAILABLE
);
break;
case HttpConstants.HTTP_METHOD_NOT_ALLOWED:
parseErrorMessageAndSetCode(
httpMethod.getResponseBodyAsString(),
ResultCode.SPECIFIC_METHOD_NOT_ALLOWED
);
default:
break;
}
}
/**
* Parse the error message included in the body response, if any, and set the specific result
* code
*
* @param bodyResponse okHttp response body
* @param resultCode our own custom result code
* @throws IOException
*/
private void parseErrorMessageAndSetCode(String bodyResponse, ResultCode resultCode) {
if (bodyResponse != null && bodyResponse.length() > 0) {
InputStream is = new ByteArrayInputStream(bodyResponse.getBytes());
ErrorMessageParser xmlParser = new ErrorMessageParser();
try {
String errorMessage = xmlParser.parseXMLResponse(is);
if (errorMessage != "" && errorMessage != null) {
mCode = resultCode;
mHttpPhrase = errorMessage;
}
} catch (Exception e) {
Log_OC.w(TAG, "Error reading exception from server: " + e.getMessage());
// mCode stays as set in this(success, httpCode, headers)
}
}
}
/**
* Public constructor from separate elements of an HTTP or DAV response.
*
* To be used when the result needs to be interpreted from HTTP response elements that could come from
* different requests (WARNING: black magic, try to avoid).
*
*
* Determines a {@link ResultCode} depending on the HTTP code and HTTP response headers received.
*
* @param httpCode HTTP status code returned by an HTTP/DAV method.
* @param httpPhrase HTTP status line phrase returned by an HTTP/DAV method
* @param headers HTTP response header returned by an HTTP/DAV method
*/
public RemoteOperationResult(int httpCode, String httpPhrase, Headers headers) {
this(httpCode, httpPhrase);
if (headers != null) {
for (Map.Entry<String, List<String>> header : headers.toMultimap().entrySet()) {
if ("location".equals(header.getKey().toLowerCase())) {
mRedirectedLocation = header.getValue().get(0);
continue;
}
if ("www-authenticate".equals(header.getKey().toLowerCase())) {
mAuthenticate.add(header.getValue().get(0).toLowerCase());
}
}
}
if (isIdPRedirection()) {
// overrides default ResultCode.UNKNOWN
mCode = ResultCode.UNAUTHORIZED;
}
}
/**
* Private constructor for results built interpreting a HTTP or DAV response.
*
* Determines a {@link ResultCode} depending of the type of the exception.
*
* @param httpCode HTTP status code returned by the HTTP/DAV method.
* @param httpPhrase HTTP status line phrase returned by the HTTP/DAV method
*/
private RemoteOperationResult(int httpCode, String httpPhrase) {
mHttpCode = httpCode;
mHttpPhrase = httpPhrase;
if (httpCode > 0) {
switch (httpCode) {
case HttpConstants.HTTP_UNAUTHORIZED: // 401
mCode = ResultCode.UNAUTHORIZED;
break;
case HttpConstants.HTTP_FORBIDDEN: // 403
mCode = ResultCode.FORBIDDEN;
break;
case HttpConstants.HTTP_NOT_FOUND: // 404
mCode = ResultCode.FILE_NOT_FOUND;
break;
case HttpConstants.HTTP_CONFLICT: // 409
mCode = ResultCode.CONFLICT;
break;
case HttpConstants.HTTP_INTERNAL_SERVER_ERROR: // 500
mCode = ResultCode.INSTANCE_NOT_CONFIGURED; // assuming too much...
break;
case HttpConstants.HTTP_SERVICE_UNAVAILABLE: // 503
mCode = ResultCode.SERVICE_UNAVAILABLE;
break;
case HttpConstants.HTTP_INSUFFICIENT_STORAGE: // 507
mCode = ResultCode.QUOTA_EXCEEDED; // surprise!
break;
default:
mCode = ResultCode.UNHANDLED_HTTP_CODE; // UNKNOWN ERROR
Log_OC.d(TAG,
"RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " +
mHttpCode + " " + mHttpPhrase
);
}
}
}
public boolean isSuccess() {
return mSuccess;
}
public boolean isCancelled() {
return mCode == ResultCode.CANCELLED;
}
public int getHttpCode() {
return mHttpCode;
}
public String getHttpPhrase() {
return mHttpPhrase;
}
public ResultCode getCode() {
return mCode;
}
public Exception getException() {
return mException;
}
public boolean isSslRecoverableException() {
return mCode == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
}
public boolean isRedirectToNonSecureConnection() {
return mCode == ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION;
}
private CertificateCombinedException getCertificateCombinedException(Exception e) {
CertificateCombinedException result = null;
if (e instanceof CertificateCombinedException) {
return (CertificateCombinedException) e;
}
Throwable cause = mException.getCause();
Throwable previousCause = null;
while (cause != null && cause != previousCause &&
!(cause instanceof CertificateCombinedException)) {
previousCause = cause;
cause = cause.getCause();
}
if (cause != null && cause instanceof CertificateCombinedException) {
result = (CertificateCombinedException) cause;
}
return result;
}
public String getLogMessage() {
if (mException != null) {
if (mException instanceof OperationCancelledException) {
return "Operation cancelled by the caller";
} else if (mException instanceof SocketException) {
return "Socket exception";
} else if (mException instanceof SocketTimeoutException) {
return "Socket timeout exception";
} else if (mException instanceof MalformedURLException) {
return "Malformed URL exception";
} else if (mException instanceof UnknownHostException) {
return "Unknown host exception";
} else if (mException instanceof CertificateCombinedException) {
if (((CertificateCombinedException) mException).isRecoverable())
return "SSL recoverable exception";
else
return "SSL exception";
} else if (mException instanceof SSLException) {
return "SSL exception";
} else if (mException instanceof DavException) {
return "Unexpected WebDAV exception";
} else if (mException instanceof HttpException) {
return "HTTP violation";
} else if (mException instanceof IOException) {
return "Unrecovered transport exception";
} else if (mException instanceof AccountUtils.AccountNotFoundException) {
Account failedAccount =
((AccountUtils.AccountNotFoundException) mException).getFailedAccount();
return mException.getMessage() + " (" +
(failedAccount != null ? failedAccount.name : "NULL") + ")";
} else if (mException instanceof AccountsException) {
return "Exception while using account";
} else if (mException instanceof JSONException) {
return "JSON exception";
} else {
return "Unexpected exception";
}
}
if (mCode == ResultCode.INSTANCE_NOT_CONFIGURED) {
return "The ownCloud server is not configured!";
} else if (mCode == ResultCode.NO_NETWORK_CONNECTION) {
return "No network connection";
} else if (mCode == ResultCode.BAD_OC_VERSION) {
return "No valid ownCloud version was found at the server";
} else if (mCode == ResultCode.LOCAL_STORAGE_FULL) {
return "Local storage full";
} else if (mCode == ResultCode.LOCAL_STORAGE_NOT_MOVED) {
return "Error while moving file to final directory";
} else if (mCode == ResultCode.ACCOUNT_NOT_NEW) {
return "Account already existing when creating a new one";
} else if (mCode == ResultCode.ACCOUNT_NOT_THE_SAME) {
return "Authenticated with a different account than the one updating";
} else if (mCode == ResultCode.INVALID_CHARACTER_IN_NAME) {
return "The file name contains an forbidden character";
} else if (mCode == ResultCode.FILE_NOT_FOUND) {
return "Local file does not exist";
} else if (mCode == ResultCode.SYNC_CONFLICT) {
return "Synchronization conflict";
}
return "Operation finished with HTTP status code " + mHttpCode + " (" +
(isSuccess() ? "success" : "fail") + ")";
}
public boolean isServerFail() {
return (mHttpCode >= HttpConstants.HTTP_INTERNAL_SERVER_ERROR);
}
public boolean isException() {
return (mException != null);
}
public boolean isTemporalRedirection() {
return (mHttpCode == 302 || mHttpCode == 307);
}
public String getRedirectedLocation() {
return mRedirectedLocation;
}
public boolean isIdPRedirection() {
return (mRedirectedLocation != null &&
(mRedirectedLocation.toUpperCase().contains("SAML") ||
mRedirectedLocation.toLowerCase().contains("wayf")));
}
/**
* Checks if is a non https connection
*
* @return boolean true/false
*/
public boolean isNonSecureRedirection() {
return (mRedirectedLocation != null && !(mRedirectedLocation.toLowerCase().startsWith("https://")));
}
public ArrayList<String> getAuthenticateHeaders() {
return mAuthenticate;
}
public String getLastPermanentLocation() {
return mLastPermanentLocation;
}
public void setLastPermanentLocation(String lastPermanentLocation) {
mLastPermanentLocation = lastPermanentLocation;
}
public void setSuccess(boolean success) {
this.mSuccess = success;
}
public void setData(T data) {
mData = data;
}
public T getData() {
return mData;
}
}
@@ -0,0 +1,206 @@
package com.owncloud.android.lib.common.utils;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
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 = 1000000; // 1MB
private static String mOwncloudDataFolderLog = "owncloud_log";
private static File mLogFile;
private static File mFolder;
private static BufferedWriter mBuf;
private static String[] mLogFileNames = {"currentLog.txt", "olderLog.txt"};
private static boolean isMaxFileSizeReached = false;
private static boolean isEnabled = false;
public static void setLogDataFolder(String logFolder){
mOwncloudDataFolderLog = logFolder;
}
public static void i(String TAG, String message){
Log.i(TAG, message);
appendLog(TAG+" : "+ message);
}
public static void d(String TAG, String message){
Log.d(TAG, message);
appendLog(TAG + " : " + message);
}
public static void d(String TAG, String message, Exception e) {
Log.d(TAG, message, e);
appendLog(TAG + " : " + message + " Exception : "+ e.getStackTrace());
}
public static void e(String TAG, String message){
Log.e(TAG, message);
appendLog(TAG + " : " + message);
}
public static void e(String TAG, String message, Throwable e) {
Log.e(TAG, message, e);
appendLog(TAG+" : " + message +" Exception : " + e.getStackTrace());
}
public static void v(String TAG, String message){
Log.v(TAG, message);
appendLog(TAG+" : "+ message);
}
public static void w(String TAG, String message) {
Log.w(TAG, message);
appendLog(TAG+" : "+ 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();
}
}
}
}
synchronized public static void stopLogging() {
try {
if (mBuf != null)
mBuf.close();
isEnabled = false;
mLogFile = null;
mFolder = null;
mBuf = null;
isMaxFileSizeReached = false;
isEnabled = 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);
}
}
/**
* Delete history logging
*/
public static void deleteHistoryLogging() {
File folderLogs = new File(mFolder + File.separator);
if(folderLogs.isDirectory()){
String[] myFiles = folderLogs.list();
for (int i=0; i<myFiles.length; i++) {
File myFile = new File(folderLogs, myFiles[i]);
myFile.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).format(Calendar.getInstance().getTime());
try {
mBuf = new BufferedWriter(new FileWriter(mLogFile, true));
mBuf.newLine();
mBuf.write(timeStamp);
mBuf.newLine();
mBuf.write(text);
mBuf.newLine();
} 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;
}
}
@@ -0,0 +1,71 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.utils;
import java.util.Random;
import java.util.UUID;
/**
* Class with methods to generate random values
*
* @author David González Verdugo
*/
public class RandomUtils {
private static final String CANDIDATECHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"1234567890-+/_=.:";
/**
* @param length the number of random chars to be generated
*
* @return String containing random chars
*/
public static String generateRandomString(int length) {
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(CANDIDATECHARS.charAt(random.nextInt(CANDIDATECHARS.length())));
}
return sb.toString();
}
/**
* @param min minimum integer to obtain randomly
* @param max maximum integer to obtain randomly
* @return random integer between min and max
*/
public static int generateRandomInteger(int min, int max) {
Random r = new Random();
return r.nextInt(max-min) + min;
}
/**
* @return random UUID
*/
public static String generateRandomUUID() {
return UUID.randomUUID().toString();
}
}
@@ -0,0 +1,142 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import android.util.Log;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.webdav.CopyMethod;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import java.net.URL;
import java.util.concurrent.TimeUnit;
/**
* Remote operation moving a remote file or folder in the ownCloud server to a different folder
* in the same account.
*
* Allows renaming the moving file/folder at the same time.
*
* @author David A. Velasco
* @author Christian Schabesberger
*/
public class CopyRemoteFileOperation extends RemoteOperation {
private static final String TAG = CopyRemoteFileOperation.class.getSimpleName();
private static final int COPY_READ_TIMEOUT = 600000;
private static final int COPY_CONNECTION_TIMEOUT = 5000;
private String mSrcRemotePath;
private String mTargetRemotePath;
private boolean mOverwrite;
/**
* Constructor.
* <p/>
* TODO Paths should finish in "/" in the case of folders. ?
*
* @param srcRemotePath Remote path of the file/folder to move.
* @param targetRemotePath Remove path desired for the file/folder after moving it.
*/
public CopyRemoteFileOperation(String srcRemotePath, String targetRemotePath, boolean overwrite
) {
mSrcRemotePath = srcRemotePath;
mTargetRemotePath = targetRemotePath;
mOverwrite = overwrite;
}
/**
* Performs the rename operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
OwnCloudVersion version = client.getOwnCloudVersion();
boolean versionWithForbiddenChars =
(version != null && version.isVersionWithForbiddenCharacters());
/// check parameters
if (!FileUtils.isValidPath(mTargetRemotePath, versionWithForbiddenChars)) {
return new RemoteOperationResult<>(ResultCode.INVALID_CHARACTER_IN_NAME);
}
if (mTargetRemotePath.equals(mSrcRemotePath)) {
// nothing to do!
return new RemoteOperationResult<>(ResultCode.OK);
}
if (mTargetRemotePath.startsWith(mSrcRemotePath)) {
return new RemoteOperationResult<>(ResultCode.INVALID_COPY_INTO_DESCENDANT);
}
/// perform remote operation
RemoteOperationResult result = null;
try {
CopyMethod copyMethod = new CopyMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mSrcRemotePath)),
client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath),
mOverwrite);
copyMethod.setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS);
copyMethod.setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
final int status = client.executeHttpMethod(copyMethod);
if(status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT) {
result = new RemoteOperationResult<>(ResultCode.OK);
} else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !mOverwrite) {
result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
client.exhaustResponse(copyMethod.getResponseBodyAsStream());
/// for other errors that could be explicitly handled, check first:
/// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
} else {
result = new RemoteOperationResult<>(copyMethod);
client.exhaustResponse(copyMethod.getResponseBodyAsStream());
}
Log.i(TAG, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " +
result.getLogMessage());
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log.e(TAG, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " +
result.getLogMessage(), e);
}
return result;
}
}
@@ -0,0 +1,127 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import android.net.Uri;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.webdav.MkColMethod;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
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 com.owncloud.android.lib.resources.status.OwnCloudVersion;
import java.net.URL;
import java.util.concurrent.TimeUnit;
/**
* Remote operation performing the creation of a new folder in the ownCloud server.
*
* @author David A. Velasco
* @author masensio
*/
public class CreateRemoteFolderOperation extends RemoteOperation {
private static final String TAG = CreateRemoteFolderOperation.class.getSimpleName();
private static final int READ_TIMEOUT = 30000;
private static final int CONNECTION_TIMEOUT = 5000;
private String mRemotePath;
private boolean mCreateFullPath;
protected boolean createChunksFolder;
/**
* Constructor
* @param remotePath Full path to the new directory to create in the remote server.
* @param createFullPath 'True' means that all the ancestor folders should be created.
*/
public CreateRemoteFolderOperation(String remotePath, boolean createFullPath) {
mRemotePath = remotePath;
mCreateFullPath = createFullPath;
createChunksFolder = false;
}
/**
* Performs the operation
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result;
OwnCloudVersion version = client.getOwnCloudVersion();
boolean versionWithForbiddenChars =
(version != null && version.isVersionWithForbiddenCharacters());
boolean noInvalidChars = FileUtils.isValidPath(mRemotePath, versionWithForbiddenChars);
if (noInvalidChars) {
result = createFolder(client);
if (!result.isSuccess() && mCreateFullPath &&
RemoteOperationResult.ResultCode.CONFLICT == result.getCode()) {
result = createParentFolder(FileUtils.getParentPath(mRemotePath), client);
if (result.isSuccess()) {
result = createFolder(client); // second (and last) try
}
}
} else {
result = new RemoteOperationResult<>(ResultCode.INVALID_CHARACTER_IN_NAME);
}
return result;
}
private RemoteOperationResult createFolder(OwnCloudClient client) {
RemoteOperationResult result;
try {
Uri webDavUri = createChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri();
final MkColMethod mkcol = new MkColMethod(new URL(webDavUri + WebdavUtils.encodePath(mRemotePath)));
mkcol.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
mkcol.setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
final int status = client.executeHttpMethod(mkcol);
result = (status == HttpConstants.HTTP_CREATED)
? new RemoteOperationResult<>(ResultCode.OK)
: new RemoteOperationResult<>(mkcol);
Log_OC.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage());
client.exhaustResponse(mkcol.getResponseBodyAsStream());
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e);
}
return result;
}
private RemoteOperationResult createParentFolder(String parentPath, OwnCloudClient client) {
RemoteOperation operation = new CreateRemoteFolderOperation(parentPath, mCreateFullPath);
return operation.execute(client);
}
}
@@ -0,0 +1,220 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod;
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.OperationCancelledException;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.net.URL;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Remote operation performing the download of a remote file in the ownCloud server.
*
* @author David A. Velasco
* @author masensio
*/
public class DownloadRemoteFileOperation extends RemoteOperation {
private static final String TAG = DownloadRemoteFileOperation.class.getSimpleName();
private static final int FORBIDDEN_ERROR = 403;
private static final int SERVICE_UNAVAILABLE_ERROR = 503;
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
private long mModificationTimestamp = 0;
private String mEtag = "";
private GetMethod mGet;
private String mRemotePath;
private String mLocalFolderPath;
public DownloadRemoteFileOperation(String remotePath, String localFolderPath) {
mRemotePath = remotePath;
mLocalFolderPath = localFolderPath;
}
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result;
/// download will be performed to a temporal file, then moved to the final location
File tmpFile = new File(getTmpPath());
/// perform the download
try {
tmpFile.getParentFile().mkdirs();
result = downloadFile(client, tmpFile);
Log_OC.i(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " +
result.getLogMessage());
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " +
result.getLogMessage(), e);
}
return result;
}
private RemoteOperationResult downloadFile(OwnCloudClient client, File targetFile) throws
Exception {
RemoteOperationResult result;
int status;
boolean savedFile = false;
mGet = new GetMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)));
Iterator<OnDatatransferProgressListener> it;
FileOutputStream fos = null;
BufferedInputStream bis = null;
try {
status = client.executeHttpMethod(mGet);
if (isSuccess(status)) {
targetFile.createNewFile();
bis = new BufferedInputStream(mGet.getResponseBodyAsStream());
fos = new FileOutputStream(targetFile);
long transferred = 0;
String contentLength = mGet.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER);
long totalToTransfer =
(contentLength != null
&& contentLength.length() > 0)
? Long.parseLong(contentLength)
: 0;
byte[] bytes = new byte[4096];
int readResult;
while ((readResult = bis.read(bytes)) != -1) {
synchronized (mCancellationRequested) {
if (mCancellationRequested.get()) {
mGet.abort();
throw new OperationCancelledException();
}
}
fos.write(bytes, 0, readResult);
transferred += readResult;
synchronized (mDataTransferListeners) {
it = mDataTransferListeners.iterator();
while (it.hasNext()) {
it.next().onTransferProgress(readResult, transferred, totalToTransfer,
targetFile.getName());
}
}
}
if (transferred == totalToTransfer) { // Check if the file is completed
savedFile = true;
final String modificationTime =
mGet.getResponseHeaders().get("Last-Modified") != null
? mGet.getResponseHeaders().get("Last-Modified")
: mGet.getResponseHeader("last-modified");
if (modificationTime != null) {
final Date d = WebdavUtils.parseResponseDate(modificationTime);
mModificationTimestamp = (d != null) ? d.getTime() : 0;
} else {
Log_OC.e(TAG, "Could not read modification time from response downloading " + mRemotePath);
}
mEtag = WebdavUtils.getEtagFromResponse(mGet);
// Get rid of extra quotas
mEtag = mEtag.replace("\"", "");
if (mEtag.length() == 0) {
Log_OC.e(TAG, "Could not read eTag from response downloading " + mRemotePath);
}
} else {
client.exhaustResponse(mGet.getResponseBodyAsStream());
// TODO some kind of error control!
}
} else if (status != FORBIDDEN_ERROR && status != SERVICE_UNAVAILABLE_ERROR) {
client.exhaustResponse(mGet.getResponseBodyAsStream());
} // else, body read by RemoteOperationResult constructor
result = isSuccess(status)
? new RemoteOperationResult<>(RemoteOperationResult.ResultCode.OK)
: new RemoteOperationResult<>(mGet);
} finally {
if (fos != null) fos.close();
if (bis != null) bis.close();
if (!savedFile && targetFile.exists()) {
targetFile.delete();
}
}
return result;
}
private boolean isSuccess(int status) {
return (status == HttpConstants.HTTP_OK);
}
private String getTmpPath() {
return mLocalFolderPath + mRemotePath;
}
public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
synchronized (mDataTransferListeners) {
mDataTransferListeners.add(listener);
}
}
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
synchronized (mDataTransferListeners) {
mDataTransferListeners.remove(listener);
}
}
public void cancel() {
mCancellationRequested.set(true); // atomic set; there is no need of synchronizing it
}
public long getModificationTimestamp() {
return mModificationTimestamp;
}
public String getEtag() {
return mEtag;
}
}
@@ -0,0 +1,153 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils;
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod;
import com.owncloud.android.lib.common.network.RedirectionPath;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Operation to check the existence or absence of a path in a remote server.
*
* @author David A. Velasco
* @author David González Verdugo
*/
public class ExistenceCheckRemoteOperation extends RemoteOperation {
/**
* Maximum time to wait for a response from the server in MILLISECONDs.
*/
public static final int TIMEOUT = 10000;
private static final String TAG = ExistenceCheckRemoteOperation.class.getSimpleName();
private String mPath;
private boolean mSuccessIfAbsent;
private boolean mIsLogin;
/**
* Sequence of redirections followed. Available only after executing the operation
*/
private RedirectionPath mRedirectionPath = null;
/**
* Full constructor. Success of the operation will depend upon the value of successIfAbsent.
*
* @param remotePath Path to append to the URL owned by the client instance.
* @param successIfAbsent When 'true', the operation finishes in success if the path does
* NOT exist in the remote server (HTTP 404).
* @param isLogin When `true`, the username won't be added at the end of the PROPFIND url since is not
* needed to check user credentials
*/
public ExistenceCheckRemoteOperation(String remotePath, boolean successIfAbsent, boolean isLogin) {
mPath = (remotePath != null) ? remotePath : "";
mSuccessIfAbsent = successIfAbsent;
mIsLogin = isLogin;
}
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
boolean previousFollowRedirects = client.followRedirects();
try {
String stringUrl = mIsLogin ?
client.getBaseFilesWebDavUri().toString() :
client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mPath);
PropfindMethod propfindMethod = new PropfindMethod(
new URL(stringUrl),
0,
DavUtils.getAllPropset()
);
propfindMethod.setReadTimeout(TIMEOUT, TimeUnit.SECONDS);
propfindMethod.setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS);
client.setFollowRedirects(false);
int status = client.executeHttpMethod(propfindMethod);
if (previousFollowRedirects) {
mRedirectionPath = client.followRedirection(propfindMethod);
status = mRedirectionPath.getLastStatus();
}
/**
* PROPFIND method
* 404 NOT FOUND: path doesn't exist,
* 207 MULTI_STATUS: path exists.
*/
Log_OC.d(TAG, "Existence check for " + stringUrl + WebdavUtils.encodePath(mPath) +
" targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") +
"finished with HTTP status " + status + (!isSuccess(status) ? "(FAIL)" : ""));
return isSuccess(status)
? new RemoteOperationResult<>(OK)
: new RemoteOperationResult<>(propfindMethod);
} catch (Exception e) {
final RemoteOperationResult result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Existence check for " + client.getUserFilesWebDavUri() +
WebdavUtils.encodePath(mPath) + " targeting for " +
(mSuccessIfAbsent ? " absence " : " existence ") + ": " +
result.getLogMessage(), result.getException());
return result;
} finally {
client.setFollowRedirects(previousFollowRedirects);
}
}
/**
* Gets the sequence of redirections followed during the execution of the operation.
*
* @return Sequence of redirections followed, if any, or NULL if the operation was not executed.
*/
public RedirectionPath getRedirectionPath() {
return mRedirectionPath;
}
/**
* @return 'True' if the operation was executed and at least one redirection was followed.
*/
public boolean wasRedirected() {
return (mRedirectionPath != null && mRedirectionPath.getRedirectionsCount() > 0);
}
private boolean isSuccess(int status) {
return ((status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS) && !mSuccessIfAbsent)
|| (status == HttpConstants.HTTP_MULTI_STATUS && !mSuccessIfAbsent)
|| (status == HttpConstants.HTTP_NOT_FOUND && mSuccessIfAbsent);
}
}
@@ -0,0 +1,84 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.File;
public class FileUtils {
private static final String TAG = FileUtils.class.getSimpleName();
public static final String PATH_SEPARATOR = "/";
public static final String FINAL_CHUNKS_FILE = ".file";
public static String getParentPath(String remotePath) {
String parentPath = new File(remotePath).getParent();
parentPath = parentPath.endsWith(PATH_SEPARATOR) ? parentPath : parentPath + PATH_SEPARATOR;
return parentPath;
}
/**
* Validate the fileName to detect if contains any forbidden character: / , \ , < , > ,
* : , " , | , ? , *
* @param fileName
* @param versionSupportsForbiddenChars
* @return
*/
public static boolean isValidName(String fileName, boolean versionSupportsForbiddenChars) {
boolean result = true;
Log_OC.d(TAG, "fileName =======" + fileName);
if ( (versionSupportsForbiddenChars && fileName.contains(PATH_SEPARATOR)) ||
(!versionSupportsForbiddenChars && ( fileName.contains(PATH_SEPARATOR) ||
fileName.contains("\\") || fileName.contains("<") || fileName.contains(">") ||
fileName.contains(":") || fileName.contains("\"") || fileName.contains("|") ||
fileName.contains("?") || fileName.contains("*") ) ) ) {
result = false;
}
return result;
}
/**
* Validate the path to detect if contains any forbidden character: \ , < , > , : , " , | ,
* ? , *
* @param path
* @return
*/
public static boolean isValidPath(String path, boolean versionSupportsForbidenChars) {
boolean result = true;
Log_OC.d(TAG, "path ....... " + path);
if (!versionSupportsForbidenChars &&
(path.contains("\\") || path.contains("<") || path.contains(">") ||
path.contains(":") || path.contains("\"") || path.contains("|") ||
path.contains("?") || path.contains("*") ) ){
result = false;
}
return result;
}
}
@@ -0,0 +1,161 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import android.net.Uri;
import android.util.Log;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import java.net.URL;
import java.util.concurrent.TimeUnit;
/**
* Remote operation moving a remote file or folder in the ownCloud server to a different folder
* in the same account.
*
* Allows renaming the moving file/folder at the same time.
*
* @author David A. Velasco
* @author David González Verdugo
*/
public class MoveRemoteFileOperation extends RemoteOperation {
private static final String TAG = MoveRemoteFileOperation.class.getSimpleName();
private static final int MOVE_READ_TIMEOUT = 600000;
private static final int MOVE_CONNECTION_TIMEOUT = 5000;
private String mSrcRemotePath;
private String mTargetRemotePath;
private boolean mOverwrite;
protected boolean moveChunkedFile = false;
protected String mFileLastModifTimestamp;
protected long mFileLength;
/**
* Constructor.
*
* TODO Paths should finish in "/" in the case of folders. ?
*
* @param srcRemotePath Remote path of the file/folder to move.
* @param targetRemotePath Remote path desired for the file/folder after moving it.
*/
public MoveRemoteFileOperation(String srcRemotePath,
String targetRemotePath,
boolean overwrite) {
mSrcRemotePath = srcRemotePath;
mTargetRemotePath = targetRemotePath;
mOverwrite = overwrite;
}
/**
* Performs the rename operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
OwnCloudVersion version = client.getOwnCloudVersion();
boolean versionWithForbiddenChars =
(version != null && version.isVersionWithForbiddenCharacters());
/// check parameters
if (!FileUtils.isValidPath(mTargetRemotePath, versionWithForbiddenChars)) {
return new RemoteOperationResult<>(ResultCode.INVALID_CHARACTER_IN_NAME);
}
if (mTargetRemotePath.equals(mSrcRemotePath)) {
// nothing to do!
return new RemoteOperationResult<>(ResultCode.OK);
}
if (mTargetRemotePath.startsWith(mSrcRemotePath)) {
return new RemoteOperationResult<>(ResultCode.INVALID_MOVE_INTO_DESCENDANT);
}
/// perform remote operation
RemoteOperationResult result;
try {
// After finishing a chunked upload, we have to move the resulting file from uploads folder to files one,
// so this uri has to be customizable
Uri srcWebDavUri = moveChunkedFile ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri();
final MoveMethod move = new MoveMethod(
new URL(srcWebDavUri + WebdavUtils.encodePath(mSrcRemotePath)),
client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath),
mOverwrite);
if (moveChunkedFile) {
move.addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, mFileLastModifTimestamp);
move.addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, String.valueOf(mFileLength));
}
move.setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS);
move.setConnectionTimeout(MOVE_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
final int status = client.executeHttpMethod(move);
/// process response
if(isSuccess(status)) {
result = new RemoteOperationResult<>(ResultCode.OK);
} else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !mOverwrite) {
result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
client.exhaustResponse(move.getResponseBodyAsStream());
/// for other errors that could be explicitly handled, check first:
/// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
} else {
result = new RemoteOperationResult<>(move);
client.exhaustResponse(move.getResponseBodyAsStream());
}
Log.i(TAG, "Move " + mSrcRemotePath + " to " + mTargetRemotePath + ": " +
result.getLogMessage());
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log.e(TAG, "Move " + mSrcRemotePath + " to " + mTargetRemotePath + ": " +
result.getLogMessage(), e);
}
return result;
}
protected boolean isSuccess(int status) {
return status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT;
}
}
@@ -0,0 +1,110 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils;
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import static com.owncloud.android.lib.common.http.methods.webdav.DavConstants.DEPTH_0;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Remote operation performing the read a file from the ownCloud server.
*
* @author David A. Velasco
* @author masensio
* @author David González Verdugo
*/
public class ReadRemoteFileOperation extends RemoteOperation<RemoteFile> {
private static final String TAG = ReadRemoteFileOperation.class.getSimpleName();
private static final int SYNC_READ_TIMEOUT = 40000;
private static final int SYNC_CONNECTION_TIMEOUT = 5000;
private String mRemotePath;
/**
* Constructor
*
* @param remotePath Remote path of the file.
*/
public ReadRemoteFileOperation(String remotePath) {
mRemotePath = remotePath;
}
/**
* Performs the read operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
protected RemoteOperationResult<RemoteFile> run(OwnCloudClient client) {
PropfindMethod propfind;
RemoteOperationResult<RemoteFile> result;
/// take the duty of check the server for the current state of the file there
try {
// remote request
propfind = new PropfindMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
DEPTH_0,
DavUtils.getAllPropset());
propfind.setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS);
propfind.setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
final int status = client.executeHttpMethod(propfind);
if (status == HttpConstants.HTTP_MULTI_STATUS
|| status == HttpConstants.HTTP_OK) {
final RemoteFile file = new RemoteFile(propfind.getRoot(), AccountUtils.getUserId(mAccount, mContext));
result = new RemoteOperationResult<>(OK);
result.setData(file);
} else {
result = new RemoteOperationResult<>(propfind);
client.exhaustResponse(propfind.getResponseBodyAsStream());
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
e.printStackTrace();
Log_OC.e(TAG, "Synchronizing file " + mRemotePath + ": " + result.getLogMessage(),
result.getException());
}
return result;
}
}
@@ -0,0 +1,130 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.http.HttpConstants;
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.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.net.URL;
import java.util.ArrayList;
import at.bitfire.dav4android.Response;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Remote operation performing the read of remote file or folder in the ownCloud server.
*
* @author David A. Velasco
* @author masensio
* @author David González Verdugo
*/
public class ReadRemoteFolderOperation extends RemoteOperation<ArrayList<RemoteFile>> {
private static final String TAG = ReadRemoteFolderOperation.class.getSimpleName();
private String mRemotePath;
/**
* Constructor
*
* @param remotePath Remote path of the file.
*/
public ReadRemoteFolderOperation(String remotePath) {
mRemotePath = remotePath;
}
/**
* Performs the read operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
protected RemoteOperationResult<ArrayList<RemoteFile>> run(OwnCloudClient client) {
RemoteOperationResult<ArrayList<RemoteFile>> result = null;
try {
PropfindMethod propfindMethod = new PropfindMethod(
new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
DavConstants.DEPTH_1,
DavUtils.getAllPropset());
client.setFollowRedirects(true);
int status = client.executeHttpMethod(propfindMethod);
if (isSuccess(status)) {
ArrayList<RemoteFile> mFolderAndFiles = new ArrayList<>();
// parse data from remote folder
mFolderAndFiles.add(
new RemoteFile(propfindMethod.getRoot(), AccountUtils.getUserId(mAccount, mContext))
);
// loop to update every child
for (Response resource : propfindMethod.getMembers()) {
RemoteFile file = new RemoteFile(resource, AccountUtils.getUserId(mAccount, mContext));
mFolderAndFiles.add(file);
}
// Result of the operation
result = new RemoteOperationResult<>(OK);
result.setData(mFolderAndFiles);
} else { // synchronization failed
result = new RemoteOperationResult<> (propfindMethod);
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
} finally {
if (result.isSuccess()) {
Log_OC.i(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage());
} else {
if (result.isException()) {
Log_OC.e(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage(),
result.getException());
} else {
Log_OC.e(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage());
}
}
}
return result;
}
private boolean isSuccess(int status) {
return status == HttpConstants.HTTP_MULTI_STATUS ||
status == HttpConstants.HTTP_OK;
}
}
@@ -0,0 +1,323 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
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 okhttp3.HttpUrl;
import static com.owncloud.android.lib.common.OwnCloudClient.WEBDAV_FILES_PATH_4_0;
/**
* Contains the data of a Remote File from a WebDavEntry
*
* @author masensio
* @author Christian Schabesberger
*/
public class RemoteFile implements Parcelable, Serializable {
/**
* Generated - should be refreshed every time the class changes!!
*/
private static final long serialVersionUID = -8965995357413958539L;
private String mRemotePath;
private String mMimeType;
private long mLength;
private long mCreationTimestamp;
private long mModifiedTimestamp;
private String mEtag;
private String mPermissions;
private String mRemoteId;
private long mSize;
private BigDecimal mQuotaUsedBytes;
private BigDecimal mQuotaAvailableBytes;
private String mPrivateLink;
/**
* Getters and Setters
*/
public String getRemotePath() {
return mRemotePath;
}
public void setRemotePath(String remotePath) {
this.mRemotePath = remotePath;
}
public String getMimeType() {
return mMimeType;
}
public void setMimeType(String mimeType) {
this.mMimeType = mimeType;
}
public long getLength() {
return mLength;
}
public void setLength(long length) {
this.mLength = length;
}
public long getCreationTimestamp() {
return mCreationTimestamp;
}
public void setCreationTimestamp(long creationTimestamp) {
this.mCreationTimestamp = creationTimestamp;
}
public long getModifiedTimestamp() {
return mModifiedTimestamp;
}
public void setModifiedTimestamp(long modifiedTimestamp) {
this.mModifiedTimestamp = modifiedTimestamp;
}
public String getEtag() {
return mEtag;
}
public void setEtag(String etag) {
this.mEtag = etag;
}
public String getPermissions() {
return mPermissions;
}
public void setPermissions(String permissions) {
this.mPermissions = permissions;
}
public String getRemoteId() {
return mRemoteId;
}
public void setRemoteId(String remoteId) {
this.mRemoteId = remoteId;
}
public long getSize() {
return mSize;
}
public void setSize(long size) {
mSize = size;
}
public void setQuotaUsedBytes(BigDecimal quotaUsedBytes) {
mQuotaUsedBytes = quotaUsedBytes;
}
public void setQuotaAvailableBytes(BigDecimal quotaAvailableBytes) {
mQuotaAvailableBytes = quotaAvailableBytes;
}
public String getPrivateLink() {
return mPrivateLink;
}
public void setPrivateLink(String privateLink) {
mPrivateLink = privateLink;
}
public RemoteFile() {
resetData();
}
/**
* Create new {@link RemoteFile} with given path.
*
* The path received must be URL-decoded. Path separator must be OCFile.PATH_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)) {
throw new IllegalArgumentException("Trying to create a OCFile with a non valid remote path: " + path);
}
mRemotePath = path;
mCreationTimestamp = 0;
mLength = 0;
mMimeType = "DIR";
mQuotaUsedBytes = BigDecimal.ZERO;
mQuotaAvailableBytes = BigDecimal.ZERO;
mPrivateLink = null;
}
public RemoteFile(final Response davResource, String userId) {
this(getRemotePathFromUrl(davResource.getHref(), userId));
final List<Property> properties = davResource.getProperties();
for(Property property : properties) {
if(property instanceof CreationDate)
this.setCreationTimestamp(
Long.parseLong(((CreationDate) property).getCreationDate()));
if(property instanceof GetContentLength)
this.setLength(((GetContentLength) property).getContentLength());
if(property instanceof GetContentType)
this.setMimeType(((GetContentType) property).getType());
if(property instanceof GetLastModified)
this.setModifiedTimestamp(((GetLastModified) property).getLastModified());
if(property instanceof GetETag)
this.setEtag(((GetETag) property).getETag());
if(property instanceof OCPermissions)
this.setPermissions(((OCPermissions) property).getPermission());
if(property instanceof OCId)
this.setRemoteId(((OCId) property).getId());
if(property instanceof OCSize)
this.setSize(((OCSize) property).getSize());
if(property instanceof QuotaUsedBytes)
this.setQuotaUsedBytes(
BigDecimal.valueOf(((QuotaUsedBytes) property).getQuotaUsedBytes()));
if(property instanceof QuotaAvailableBytes)
this.setQuotaAvailableBytes(
BigDecimal.valueOf(((QuotaAvailableBytes) property).getQuotaAvailableBytes()));
if(property instanceof OCPrivatelink)
this.setPrivateLink(((OCPrivatelink) property).getLink());
}
}
/**
* Retrieves a relative path from a remote file url
*
* Example: url:port/remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt
*
* @param url remote file url
* @param userId file owner
* @return remote relative path of the file
*/
private static String getRemotePathFromUrl(HttpUrl url, String userId) {
final String davFilesPath = WEBDAV_FILES_PATH_4_0 + userId;
final String absoluteDavPath = Uri.decode(url.encodedPath());
final String pathToOc = absoluteDavPath.split(davFilesPath)[0];
return absoluteDavPath.replace(pathToOc + davFilesPath, "");
}
/**
* Used internally. Reset all file properties
*/
private void resetData() {
mRemotePath = null;
mMimeType = null;
mLength = 0;
mCreationTimestamp = 0;
mModifiedTimestamp = 0;
mEtag = null;
mPermissions = null;
mRemoteId = null;
mSize = 0;
mQuotaUsedBytes = null;
mQuotaAvailableBytes = null;
mPrivateLink = null;
}
/**
* Parcelable Methods
*/
public static final Parcelable.Creator<RemoteFile> CREATOR = new Parcelable.Creator<RemoteFile>() {
@Override
public RemoteFile createFromParcel(Parcel source) {
return new RemoteFile(source);
}
@Override
public RemoteFile[] newArray(int size) {
return new RemoteFile[size];
}
};
/**
* Reconstruct from parcel
*
* @param source The source parcel
*/
protected RemoteFile(Parcel source) {
readFromParcel(source);
}
public void readFromParcel(Parcel source) {
mRemotePath = source.readString();
mMimeType = source.readString();
mLength = source.readLong();
mCreationTimestamp = source.readLong();
mModifiedTimestamp = source.readLong();
mEtag = source.readString();
mPermissions = source.readString();
mRemoteId = source.readString();
mSize = source.readLong();
mQuotaUsedBytes = (BigDecimal) source.readSerializable();
mQuotaAvailableBytes = (BigDecimal) source.readSerializable();
mPrivateLink = source.readString();
}
@Override
public int describeContents() {
return this.hashCode();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mRemotePath);
dest.writeString(mMimeType);
dest.writeLong(mLength);
dest.writeLong(mCreationTimestamp);
dest.writeLong(mModifiedTimestamp);
dest.writeString(mEtag);
dest.writeString(mPermissions);
dest.writeString(mRemoteId);
dest.writeLong(mSize);
dest.writeSerializable(mQuotaUsedBytes);
dest.writeSerializable(mQuotaAvailableBytes);
dest.writeString(mPrivateLink);
}
}
@@ -0,0 +1,97 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import android.net.Uri;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.nonwebdav.DeleteMethod;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.net.URL;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Remote operation performing the removal of a remote file or folder in the ownCloud server.
*
* @author David A. Velasco
* @author masensio
* @author David González Verdugo
*/
public class RemoveRemoteFileOperation extends RemoteOperation {
private static final String TAG = RemoveRemoteFileOperation.class.getSimpleName();
private String mRemotePath;
protected boolean removeChunksFolder = false;
/**
* Constructor
*
* @param remotePath RemotePath of the remote file or folder to remove from the server
*/
public RemoveRemoteFileOperation(String remotePath) {
mRemotePath = remotePath;
}
/**
* Performs the rename operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result;
try {
Uri srcWebDavUri = removeChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri();
DeleteMethod deleteMethod = new DeleteMethod(
new URL(srcWebDavUri + WebdavUtils.encodePath(mRemotePath)));
int status = client.executeHttpMethod(deleteMethod);
result = isSuccess(status) ?
new RemoteOperationResult<>(OK) :
new RemoteOperationResult<>(deleteMethod);
Log_OC.i(TAG, "Remove " + mRemotePath + ": " + result.getLogMessage());
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Remove " + mRemotePath + ": " + result.getLogMessage(), e);
}
return result;
}
private boolean isSuccess(int status) {
return status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_NO_CONTENT;
}
}
@@ -0,0 +1,145 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import java.io.File;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
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 com.owncloud.android.lib.resources.status.OwnCloudVersion;
/**
* Remote operation performing the rename of a remote file or folder in the ownCloud server.
*
* @author David A. Velasco
* @author masensio
*/
public class RenameRemoteFileOperation extends RemoteOperation {
private static final String TAG = RenameRemoteFileOperation.class.getSimpleName();
private static final int RENAME_READ_TIMEOUT = 600000;
private static final int RENAME_CONNECTION_TIMEOUT = 5000;
private String mOldName;
private String mOldRemotePath;
private String mNewName;
private String mNewRemotePath;
/**
* Constructor
*
* @param oldName Old name of the file.
* @param oldRemotePath Old remote path of the file.
* @param newName New name to set as the name of file.
* @param isFolder 'true' for folder and 'false' for files
*/
public RenameRemoteFileOperation(String oldName, String oldRemotePath, String newName,
boolean isFolder) {
mOldName = oldName;
mOldRemotePath = oldRemotePath;
mNewName = newName;
String parent = (new File(mOldRemotePath)).getParent();
parent = (parent.endsWith(FileUtils.PATH_SEPARATOR)) ? parent : parent +
FileUtils.PATH_SEPARATOR;
mNewRemotePath = parent + mNewName;
if (isFolder) {
mNewRemotePath += FileUtils.PATH_SEPARATOR;
}
}
/**
* Performs the rename operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
final OwnCloudVersion version = client.getOwnCloudVersion();
final boolean versionWithForbiddenChars =
(version != null && version.isVersionWithForbiddenCharacters());
if(!FileUtils.isValidPath(mNewRemotePath, versionWithForbiddenChars))
return new RemoteOperationResult<>(ResultCode.INVALID_CHARACTER_IN_NAME);
try {
if (mNewName.equals(mOldName)) {
return new RemoteOperationResult<>(ResultCode.OK);
}
if (targetPathIsUsed(client)) {
return new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
}
final MoveMethod move = new MoveMethod(new URL(client.getUserFilesWebDavUri() +
WebdavUtils.encodePath(mOldRemotePath)),
client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mNewRemotePath), false);
move.setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.SECONDS);
move.setConnectionTimeout(RENAME_READ_TIMEOUT, TimeUnit.SECONDS);
final int status = client.executeHttpMethod(move);
final RemoteOperationResult result =
(status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT)
? new RemoteOperationResult<>(ResultCode.OK)
: new RemoteOperationResult<>(move);
Log_OC.i(TAG, "Rename " + mOldRemotePath + " to " + mNewRemotePath + ": " +
result.getLogMessage()
);
client.exhaustResponse(move.getResponseBodyAsStream());
return result;
} catch (Exception e) {
final RemoteOperationResult result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Rename " + mOldRemotePath + " to " +
((mNewRemotePath == null) ? mNewName : mNewRemotePath) + ": " +
result.getLogMessage(), e);
return result;
}
}
/**
* Checks if a file with the new name already exists.
*
* @return 'True' if the target path is already used by an existing file.
*/
private boolean targetPathIsUsed(OwnCloudClient client) {
ExistenceCheckRemoteOperation existenceCheckRemoteOperation =
new ExistenceCheckRemoteOperation(mNewRemotePath, false, false);
RemoteOperationResult exists = existenceCheckRemoteOperation.run(client);
return exists.isSuccess();
}
}
@@ -0,0 +1,187 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpConstants;
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod;
import com.owncloud.android.lib.common.network.FileRequestBody;
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.operations.OperationCancelledException;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.File;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import okhttp3.MediaType;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Remote operation performing the upload of a remote file to the ownCloud server.
*
* @author David A. Velasco
* @author masensio
* @author David González Verdugo
*/
public class UploadRemoteFileOperation extends RemoteOperation {
private static final String TAG = UploadRemoteFileOperation.class.getSimpleName();
protected String mLocalPath;
protected String mRemotePath;
protected String mMimeType;
protected String mFileLastModifTimestamp;
protected PutMethod mPutMethod = null;
protected String mRequiredEtag = null;
protected final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
protected Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
protected FileRequestBody mFileRequestBody = null;
public UploadRemoteFileOperation(String localPath, String remotePath, String mimeType,
String fileLastModifTimestamp) {
mLocalPath = localPath;
mRemotePath = remotePath;
mMimeType = mimeType;
mFileLastModifTimestamp = fileLastModifTimestamp;
}
public UploadRemoteFileOperation(String localPath, String remotePath, String mimeType,
String requiredEtag, String fileLastModifTimestamp) {
this(localPath, remotePath, mimeType, fileLastModifTimestamp);
mRequiredEtag = requiredEtag;
}
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
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
result = new RemoteOperationResult<>(new OperationCancelledException());
} else {
// perform the upload
result = uploadFile(client);
Log_OC.i(TAG, "Upload of " + mLocalPath + " to " + mRemotePath + ": " +
result.getLogMessage());
}
} catch (Exception e) {
if (mPutMethod != null && mPutMethod.isAborted()) {
result = new RemoteOperationResult<>(new OperationCancelledException());
Log_OC.e(TAG, "Upload of " + mLocalPath + " to " + mRemotePath + ": " +
result.getLogMessage(), new OperationCancelledException());
} else {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Upload of " + mLocalPath + " to " + mRemotePath + ": " +
result.getLogMessage(), e);
}
}
return result;
}
protected RemoteOperationResult<? extends Object> uploadFile(OwnCloudClient client) throws Exception {
File fileToUpload = new File(mLocalPath);
MediaType mediaType = MediaType.parse(mMimeType);
mFileRequestBody = new FileRequestBody(fileToUpload, mediaType);
synchronized (mDataTransferListeners) {
mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners);
}
if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
mPutMethod.addRequestHeader(HttpConstants.IF_MATCH_HEADER, mRequiredEtag);
}
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)) {
return new RemoteOperationResult<>(OK);
} else { // synchronization failed
return new RemoteOperationResult<>(mPutMethod);
}
}
public Set<OnDatatransferProgressListener> getDataTransferListeners() {
return mDataTransferListeners;
}
public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
synchronized (mDataTransferListeners) {
mDataTransferListeners.add(listener);
}
if (mFileRequestBody != null) {
mFileRequestBody.addDatatransferProgressListener(listener);
}
}
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
synchronized (mDataTransferListeners) {
mDataTransferListeners.remove(listener);
}
if (mFileRequestBody != null) {
mFileRequestBody.removeDatatransferProgressListener(listener);
}
}
public void cancel() {
synchronized (mCancellationRequested) {
mCancellationRequested.set(true);
if (mPutMethod != null)
mPutMethod.abort();
}
}
public boolean isSuccess(int status) {
return ((status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_CREATED ||
status == HttpConstants.HTTP_NO_CONTENT));
}
}
@@ -0,0 +1,138 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files.chunks;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod;
import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody;
import com.owncloud.android.lib.common.operations.OperationCancelledException;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.FileUtils;
import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.util.concurrent.TimeUnit;
import okhttp3.MediaType;
import static com.owncloud.android.lib.common.http.HttpConstants.IF_MATCH_HEADER;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Remote operation performing the chunked upload of a remote file to the ownCloud server.
*
* @author David A. Velasco
* @author David González Verdugo
*/
public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation {
private static final int LAST_CHUNK_TIMEOUT = 900000; //15 mins.
public static final long CHUNK_SIZE = 1024000;
private static final String TAG = ChunkedUploadRemoteFileOperation.class.getSimpleName();
private String mTransferId;
public ChunkedUploadRemoteFileOperation(String transferId, String localPath, String remotePath, String mimeType,
String requiredEtag, String fileLastModifTimestamp) {
super(localPath, remotePath, mimeType, requiredEtag, fileLastModifTimestamp);
mTransferId = transferId;
}
@Override
protected RemoteOperationResult uploadFile(OwnCloudClient client) throws Exception {
int status;
RemoteOperationResult result = null;
FileChannel channel;
RandomAccessFile raf;
File fileToUpload = new File(mLocalPath);
MediaType mediaType = MediaType.parse(mMimeType);
raf = new RandomAccessFile(fileToUpload, "r");
channel = raf.getChannel();
mFileRequestBody = new ChunkFromFileRequestBody(fileToUpload, mediaType, channel, CHUNK_SIZE);
synchronized (mDataTransferListeners) {
mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners);
}
long offset = 0;
String uriPrefix = client.getUploadsWebDavUri() + FileUtils.PATH_SEPARATOR + String.valueOf(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);
if (mCancellationRequested.get()) {
result = new RemoteOperationResult<>(new OperationCancelledException());
break;
} else {
if (chunkIndex == chunkCount - 1) {
// Added a high timeout to the last chunk due to when the last chunk
// arrives to the server with the last PUT, all chunks get assembled
// within that PHP request, so last one takes longer.
mPutMethod.setReadTimeout(LAST_CHUNK_TIMEOUT, TimeUnit.MILLISECONDS);
}
mPutMethod.setRequestBody(mFileRequestBody);
status = client.executeHttpMethod(mPutMethod);
Log_OC.d(TAG, "Upload of " + mLocalPath + " to " + mRemotePath +
", chunk index " + chunkIndex + ", count " + chunkCount +
", HTTP result status " + status);
if (isSuccess(status)) {
result = new RemoteOperationResult<>(OK);
} else {
result = new RemoteOperationResult<>(mPutMethod);
break;
}
}
}
if (channel != null)
channel.close();
if (raf != null)
raf.close();
return result;
}
}
@@ -0,0 +1,45 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files.chunks;
import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation;
/**
* Remote operation performing the creation of a new folder to save chunks during an upload to the ownCloud server.
*
* @author David González Verdugo
*/
public class CreateRemoteChunkFolderOperation extends CreateRemoteFolderOperation {
/**
* Constructor
*
* @param remotePath Full path to the new directory to create in the remote server.
* @param createFullPath 'True' means that all the ancestor folders should be created.
*/
public CreateRemoteChunkFolderOperation(String remotePath, boolean createFullPath) {
super(remotePath, createFullPath);
createChunksFolder = true;
}
}
@@ -0,0 +1,50 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files.chunks;
import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation;
/**
* Remote operation to move the file built from chunks after uploading it
*
* @author David González Verdugo
*/
public class MoveRemoteChunksFileOperation extends MoveRemoteFileOperation {
/**
* Constructor.
*
* @param srcRemotePath Remote path of the file/folder to move.
* @param targetRemotePath Remove path desired for the file/folder after moving it.
* @param overwrite
*/
public MoveRemoteChunksFileOperation(String srcRemotePath, String targetRemotePath, boolean overwrite,
String fileLastModifTimestamp, long fileLength) {
super(srcRemotePath, targetRemotePath, overwrite);
moveChunkedFile = true;
mFileLastModifTimestamp = fileLastModifTimestamp;
mFileLength = fileLength;
}
}
@@ -0,0 +1,41 @@
/* ownCloud Android Library is available under MIT license
* @author David González Verdugo
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.files.chunks;
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation;
public class RemoveRemoteChunksFolderOperation extends RemoveRemoteFileOperation {
/**
* Constructor
*
* @param remotePath RemotePath of the remote file or folder to remove from the server
*/
public RemoveRemoteChunksFolderOperation(String remotePath) {
super(remotePath);
removeChunksFolder = true;
}
}
@@ -0,0 +1,273 @@
/* ownCloud Android Library is available under MIT license
* @author masensio
* @author David A. Velasco
* @author David González Verdugo
* Copyright (C) 2018 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.PostMethod;
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;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import okhttp3.FormBody;
/**
* Creates a new share. This allows sharing with a user or group or as a link.
*
* @author masensio
* @author David A. Velasco
* @author David González Verdugo
*/
public class CreateRemoteShareOperation extends RemoteOperation {
private static final String TAG = CreateRemoteShareOperation.class.getSimpleName();
private static final String PARAM_NAME = "name";
private static final String PARAM_PASSWORD = "password";
private static final String PARAM_EXPIRATION_DATE = "expireDate";
private static final String PARAM_PUBLIC_UPLOAD = "publicUpload";
private static final String PARAM_PATH = "path";
private static final String PARAM_SHARE_TYPE = "shareType";
private static final String PARAM_SHARE_WITH = "shareWith";
private static final String PARAM_PERMISSIONS = "permissions";
private static final String FORMAT_EXPIRATION_DATE = "yyyy-MM-dd";
private String mRemoteFilePath;
private ShareType mShareType;
private String mShareWith;
private boolean mGetShareDetails;
/**
* Name to set for the public link
*/
private String mName = "";
/**
* Password to set for the public link
*/
private String mPassword;
/**
* Expiration date to set for the public link
*/
private long mExpirationDateInMillis = 0;
/**
* Access permissions for the file bound to the share
*/
private int mPermissions;
/**
* Upload permissions for the public link (only folders)
*/
private Boolean mPublicUpload;
/**
* Constructor
*
* @param remoteFilePath Full path of the file/folder being shared. Mandatory argument
* @param shareType 0 = user, 1 = group, 3 = Public link. Mandatory argument
* @param shareWith User/group ID with who the file should be shared. This is mandatory for shareType
* of 0 or 1
* @param publicUpload If false (default) public cannot upload to a public shared folder.
* If true public can upload to a shared folder. Only available for public link shares
* @param password Password to protect a public link share. Only available for public link shares
* @param permissions 1 - Read only Default for public shares
* 2 - Update
* 4 - Create
* 8 - Delete
* 16- Re-share
* 31- All above Default for private shares
* For user or group shares.
* To obtain combinations, add the desired values together.
* For instance, for Re-Share, delete, read, update, add 16+8+2+1 = 27.
*/
public CreateRemoteShareOperation(
String remoteFilePath,
ShareType shareType,
String shareWith,
boolean publicUpload,
String password,
int permissions
) {
mRemoteFilePath = remoteFilePath;
mShareType = shareType;
mShareWith = shareWith;
mPublicUpload = publicUpload;
mPassword = password;
mPermissions = permissions;
mGetShareDetails = false; // defaults to false for backwards compatibility
}
/**
* Set name to create in Share resource. Ignored by servers previous to version 10.0.0
*
* @param name Name to set to the target share.
* Null or empty string result in no value set for the name.
*/
public void setName(String name) {
this.mName = (name == null) ? "" : name;
}
/**
* Set password to create in Share resource.
*
* @param password Password to set to the target share.
* Null or empty string result in no value set for the password.
*/
public void setPassword(String password) {
mPassword = password;
}
/**
* Set expiration date to create in Share resource.
*
* @param expirationDateInMillis Expiration date to set to the target share.
* Zero or negative value results in no value sent for expiration date.
*/
public void setExpirationDate(long expirationDateInMillis) {
mExpirationDateInMillis = expirationDateInMillis;
}
/**
* Set permissions to create in Share resource.
*
* @param permissions Permissions to set to the target share.
* Values <= 0 result in value set to the permissions.
*/
public void setPermissions(int permissions) {
mPermissions = permissions;
}
/**
* * Enable upload permissions to create in Share resource.
* *
* * @param publicUpload Upload permission to set to the target share.
* * Null results in no update applied to the upload permission.
*/
public void setPublicUpload(Boolean publicUpload) {
mPublicUpload = publicUpload;
}
public void setGetShareDetails(boolean set) {
mGetShareDetails = set;
}
@Override
protected RemoteOperationResult<ShareParserResult> run(OwnCloudClient client) {
RemoteOperationResult<ShareParserResult> result;
try {
FormBody.Builder formBodyBuilder = new FormBody.Builder()
.add(PARAM_PATH, mRemoteFilePath)
.add(PARAM_SHARE_TYPE, Integer.toString(mShareType.getValue()))
.add(PARAM_SHARE_WITH, mShareWith);
if (mName.length() > 0) {
formBodyBuilder.add(PARAM_NAME, mName);
}
if (mExpirationDateInMillis > 0) {
DateFormat dateFormat = new SimpleDateFormat(FORMAT_EXPIRATION_DATE, Locale.getDefault());
Calendar expirationDate = Calendar.getInstance();
expirationDate.setTimeInMillis(mExpirationDateInMillis);
String formattedExpirationDate = dateFormat.format(expirationDate.getTime());
formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate);
}
if (mPublicUpload) {
formBodyBuilder.add(PARAM_PUBLIC_UPLOAD, Boolean.toString(true));
}
if (mPassword != null && mPassword.length() > 0) {
formBodyBuilder.add(PARAM_PASSWORD, mPassword);
}
if (OCShare.DEFAULT_PERMISSION != mPermissions) {
formBodyBuilder.add(PARAM_PERMISSIONS, Integer.toString(mPermissions));
}
Uri requestUri = client.getBaseUri();
Uri.Builder uriBuilder = requestUri.buildUpon();
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH);
PostMethod postMethod = new PostMethod(new URL(uriBuilder.build().toString()));
postMethod.setRequestBody(formBodyBuilder.build());
postMethod.setRequestHeader(HttpConstants.CONTENT_TYPE_HEADER, HttpConstants.CONTENT_TYPE_URLENCODED_UTF8);
postMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE);
int status = client.executeHttpMethod(postMethod);
ShareToRemoteOperationResultParser parser = new ShareToRemoteOperationResultParser(
new ShareXMLParser()
);
if (isSuccess(status)) {
parser.setOneOrMoreSharesRequired(true);
parser.setOwnCloudVersion(client.getOwnCloudVersion());
parser.setServerBaseUri(client.getBaseUri());
result = parser.parse(postMethod.getResponseBodyAsString());
if (result.isSuccess() && mGetShareDetails) {
// TODO Use executeHttpMethod
// retrieve more info - POST only returns the index of the new share
OCShare emptyShare = result.getData().getShares().get(0);
GetRemoteShareOperation getInfo = new GetRemoteShareOperation(
emptyShare.getRemoteId()
);
result = getInfo.execute(client);
}
} else {
result = parser.parse(postMethod.getResponseBodyAsString());
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Exception while Creating New Share", e);
}
return result;
}
private boolean isSuccess(int status) {
return (status == HttpConstants.HTTP_OK);
}
}
@@ -0,0 +1,97 @@
/* ownCloud Android Library is available under MIT license
* @author David A. Velasco
* @author David González Verdugo
* Copyright (C) 2018 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 about a Share resource, known its remote ID.
*
* @author David A. Velasco
* @author David González Verdugo
*/
public class GetRemoteShareOperation extends RemoteOperation<ShareParserResult> {
private static final String TAG = GetRemoteShareOperation.class.getSimpleName();
private long mRemoteId;
public GetRemoteShareOperation(long remoteId) {
mRemoteId = remoteId;
}
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult<ShareParserResult> result;
try {
Uri requestUri = client.getBaseUri();
Uri.Builder uriBuilder = requestUri.buildUpon();
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH);
uriBuilder.appendEncodedPath(Long.toString(mRemoteId));
GetMethod getMethod = new GetMethod(new URL(uriBuilder.build().toString()));
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.setOneOrMoreSharesRequired(true);
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);
}
}
@@ -0,0 +1,198 @@
/* ownCloud Android Library is available under MIT license
*
* @author masensio
* @author David A. Velasco
* @author David González Verdugo
* Copyright (C) 2018 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 org.json.JSONArray;
import org.json.JSONObject;
import java.net.URL;
import java.util.ArrayList;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Created by masensio on 08/10/2015.
*
* Retrieves a list of sharees (possible targets of a share) from the ownCloud server.
*
* Currently only handles users and groups. Users in other OC servers (federation) should be added later.
*
* Depends on SHAREE API. {@See https://github.com/owncloud/documentation/issues/1626}
*
* Syntax:
* Entry point: ocs/v2.php/apps/files_sharing/api/v1/sharees
* HTTP method: GET
* url argument: itemType - string, required
* url argument: format - string, optional
* url argument: search - string, optional
* url arguments: perPage - int, optional
* url arguments: page - int, optional
*
* Status codes:
* 100 - successful
*
* @author masensio
* @author David A. Velasco
* @author David González Verdugo
*/
public class GetRemoteShareesOperation extends RemoteOperation<ArrayList<JSONObject>> {
private static final String TAG = GetRemoteShareesOperation.class.getSimpleName();
// OCS Routes
private static final String OCS_ROUTE = "ocs/v2.php/apps/files_sharing/api/v1/sharees"; // from OC 8.2
// Arguments - names
private static final String PARAM_FORMAT = "format";
private static final String PARAM_ITEM_TYPE = "itemType";
private static final String PARAM_SEARCH = "search";
private static final String PARAM_PAGE = "page"; // default = 1
private static final String PARAM_PER_PAGE = "perPage"; // default = 200
// Arguments - constant values
private static final String VALUE_FORMAT = "json";
private static final String VALUE_ITEM_TYPE = "file"; // to get the server search for users / groups
// JSON Node names
private static final String NODE_OCS = "ocs";
private static final String NODE_DATA = "data";
private static final String NODE_EXACT = "exact";
private static final String NODE_USERS = "users";
private static final String NODE_GROUPS = "groups";
private static final String NODE_REMOTES = "remotes";
public static final String NODE_VALUE = "value";
public static final String PROPERTY_LABEL = "label";
public static final String PROPERTY_SHARE_TYPE = "shareType";
public static final String PROPERTY_SHARE_WITH = "shareWith";
private String mSearchString;
private int mPage;
private int mPerPage;
/**
* Constructor
*
* @param searchString string for searching users, optional
* @param page page index in the list of results; beginning in 1
* @param perPage maximum number of results in a single page
*/
public GetRemoteShareesOperation(String searchString, int page, int perPage) {
mSearchString = searchString;
mPage = page;
mPerPage = perPage;
}
@Override
protected RemoteOperationResult<ArrayList<JSONObject>> run(OwnCloudClient client) {
RemoteOperationResult<ArrayList<JSONObject>> result;
try{
Uri requestUri = client.getBaseUri();
Uri.Builder uriBuilder = requestUri.buildUpon()
.appendEncodedPath(OCS_ROUTE)
.appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT)
.appendQueryParameter(PARAM_ITEM_TYPE, VALUE_ITEM_TYPE)
.appendQueryParameter(PARAM_SEARCH, mSearchString)
.appendQueryParameter(PARAM_PAGE, String.valueOf(mPage))
.appendQueryParameter(PARAM_PER_PAGE, String.valueOf(mPerPage));
GetMethod getMethod = new GetMethod(new URL(uriBuilder.build().toString()));
getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE);
int status = client.executeHttpMethod(getMethod);
String response = getMethod.getResponseBodyAsString();
if(isSuccess(status)) {
Log_OC.d(TAG, "Successful response: " + response);
// Parse the response
JSONObject respJSON = new JSONObject(response);
JSONObject respOCS = respJSON.getJSONObject(NODE_OCS);
JSONObject respData = respOCS.getJSONObject(NODE_DATA);
JSONObject respExact = respData.getJSONObject(NODE_EXACT);
JSONArray respExactUsers = respExact.getJSONArray(NODE_USERS);
JSONArray respExactGroups = respExact.getJSONArray(NODE_GROUPS);
JSONArray respExactRemotes = respExact.getJSONArray(NODE_REMOTES);
JSONArray respPartialUsers = respData.getJSONArray(NODE_USERS);
JSONArray respPartialGroups = respData.getJSONArray(NODE_GROUPS);
JSONArray respPartialRemotes = respData.getJSONArray(NODE_REMOTES);
JSONArray[] jsonResults = {
respExactUsers,
respExactGroups,
respExactRemotes,
respPartialUsers,
respPartialGroups,
respPartialRemotes
};
ArrayList<JSONObject> data = new ArrayList<>(); // For result data
for (int i=0; i<6; i++) {
for(int j=0; j< jsonResults[i].length(); j++){
JSONObject jsonResult = jsonResults[i].getJSONObject(j);
data.add(jsonResult);
Log_OC.d(TAG, "*** Added item: " + jsonResult.getString(PROPERTY_LABEL));
}
}
result = new RemoteOperationResult<>(OK);
result.setData(data);
Log_OC.d(TAG, "*** Get Users or groups completed " );
} else {
result = new RemoteOperationResult<>(getMethod);
Log_OC.e(TAG, "Failed response while getting users/groups from the server ");
if (response != null) {
Log_OC.e(TAG, "*** status code: " + status + "; response message: " + response);
} else {
Log_OC.e(TAG, "*** status code: " + status);
}
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Exception while getting users/groups", e);
}
return result;
}
private boolean isSuccess(int status) {
return (status == HttpConstants.HTTP_OK);
}
}
@@ -0,0 +1,124 @@
/* ownCloud Android Library is available under MIT license
* @author masensio
* @author David A. Velasco
* @author David González Verdugo
* Copyright (C) 2018 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;
/**
* Provide a list shares for a specific file.
* The input is the full path of the desired file.
* The output is a list of everyone who has the file shared with them.
*
* @author masensio
* @author David A. Velasco
* @author David González Verdugo
*/
public class GetRemoteSharesForFileOperation extends RemoteOperation<ShareParserResult> {
private static final String TAG = GetRemoteSharesForFileOperation.class.getSimpleName();
private static final String PARAM_PATH = "path";
private static final String PARAM_RESHARES = "reshares";
private static final String PARAM_SUBFILES = "subfiles";
private String mRemoteFilePath;
private boolean mReshares;
private boolean mSubfiles;
/**
* Constructor
*
* @param remoteFilePath Path to file or folder
* @param reshares If set to false (default), only shares owned by the current user are
* returned.
* If set to true, shares owned by any user from the given file are returned.
* @param subfiles If set to false (default), lists only the folder being shared
* If set to true, all shared files within the folder are returned.
*/
public GetRemoteSharesForFileOperation(String remoteFilePath, boolean reshares,
boolean subfiles) {
mRemoteFilePath = remoteFilePath;
mReshares = reshares;
mSubfiles = subfiles;
}
@Override
protected RemoteOperationResult<ShareParserResult> run(OwnCloudClient client) {
RemoteOperationResult<ShareParserResult> result;
try {
Uri requestUri = client.getBaseUri();
Uri.Builder uriBuilder = requestUri.buildUpon();
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH);
uriBuilder.appendQueryParameter(PARAM_PATH, mRemoteFilePath);
uriBuilder.appendQueryParameter(PARAM_RESHARES, String.valueOf(mReshares));
uriBuilder.appendQueryParameter(PARAM_SUBFILES, String.valueOf(mSubfiles));
GetMethod getMethod = new GetMethod(new URL(uriBuilder.build().toString()));
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());
if (result.isSuccess()) {
Log_OC.d(TAG, "Got " + result.getData().getShares().size() + " shares");
}
} else {
result = new RemoteOperationResult<>(getMethod);
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Exception while getting shares", e);
}
return result;
}
private boolean isSuccess(int status) {
return (status == HttpConstants.HTTP_OK);
}
}
@@ -0,0 +1,91 @@
/* ownCloud Android Library is available under MIT license
*
* Copyright (C) 2018 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);
}
}
@@ -0,0 +1,344 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2017 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.shares;
import java.io.File;
import java.io.Serializable;
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;
/**
* Contains the data of a Share from the Share API
*
* @author masensio
* @author David A. Velasco
*/
public class OCShare implements Parcelable, Serializable {
/**
* Generated - should be refreshed every time the class changes!!
*/
private static final long serialVersionUID = 4124975224281327921L;
private static final String TAG = OCShare.class.getSimpleName();
public static final int DEFAULT_PERMISSION = -1;
public static final int READ_PERMISSION_FLAG = 1;
public static final int UPDATE_PERMISSION_FLAG = 2;
public static final int CREATE_PERMISSION_FLAG = 4;
public static final int DELETE_PERMISSION_FLAG = 8;
public static final int SHARE_PERMISSION_FLAG = 16;
public static final int MAXIMUM_PERMISSIONS_FOR_FILE =
READ_PERMISSION_FLAG +
UPDATE_PERMISSION_FLAG +
SHARE_PERMISSION_FLAG;
public static final int MAXIMUM_PERMISSIONS_FOR_FOLDER =
MAXIMUM_PERMISSIONS_FOR_FILE +
CREATE_PERMISSION_FLAG +
DELETE_PERMISSION_FLAG;
public static final int FEDERATED_PERMISSIONS_FOR_FILE_UP_TO_OC9 =
READ_PERMISSION_FLAG +
UPDATE_PERMISSION_FLAG;
public static final int FEDERATED_PERMISSIONS_FOR_FILE_AFTER_OC9 =
READ_PERMISSION_FLAG +
UPDATE_PERMISSION_FLAG +
SHARE_PERMISSION_FLAG;
public static final int FEDERATED_PERMISSIONS_FOR_FOLDER_UP_TO_OC9 =
READ_PERMISSION_FLAG +
UPDATE_PERMISSION_FLAG +
CREATE_PERMISSION_FLAG +
DELETE_PERMISSION_FLAG;
public static final int FEDERATED_PERMISSIONS_FOR_FOLDER_AFTER_OC9 =
FEDERATED_PERMISSIONS_FOR_FOLDER_UP_TO_OC9 +
SHARE_PERMISSION_FLAG;
private long mId;
private long mFileSource;
private long mItemSource;
private ShareType mShareType;
private String mShareWith;
private String mPath;
private int mPermissions;
private long mSharedDate;
private long mExpirationDate;
private String mToken;
private String mSharedWithDisplayName;
private String mName;
private boolean mIsFolder;
private long mUserId;
private long mRemoteId;
private String mShareLink;
public OCShare() {
super();
resetData();
}
public OCShare(String path) {
resetData();
if (path == null || path.length() <= 0 || !path.startsWith(FileUtils.PATH_SEPARATOR)) {
Log_OC.e(TAG, "Trying to create a OCShare with a non valid path");
throw new IllegalArgumentException("Trying to create a OCShare with a non valid path: " + path);
}
mPath = path;
}
/**
* Used internally. Reset all file properties
*/
private void resetData() {
mId = -1;
mFileSource = 0;
mItemSource = 0;
mShareType = ShareType.NO_SHARED;
mShareWith = "";
mPath = "";
mPermissions = -1;
mSharedDate = 0;
mExpirationDate = 0;
mToken = "";
mSharedWithDisplayName = "";
mIsFolder = false;
mUserId = -1;
mRemoteId = -1;
mShareLink = "";
mName = "";
}
/// Getters and Setters
public long getId() {
return mId;
}
public void setId(long id) {
mId = id;
}
public long getFileSource() {
return mFileSource;
}
public void setFileSource(long fileSource) {
this.mFileSource = fileSource;
}
public long getItemSource() {
return mItemSource;
}
public void setItemSource(long itemSource) {
this.mItemSource = itemSource;
}
public ShareType getShareType() {
return mShareType;
}
public void setShareType(ShareType shareType) {
this.mShareType = shareType;
}
public String getShareWith() {
return mShareWith;
}
public void setShareWith(String shareWith) {
this.mShareWith = (shareWith != null) ? shareWith : "";
}
public String getPath() {
return mPath;
}
public void setPath(String path) {
this.mPath = (path != null) ? path : "";
}
public int getPermissions() {
return mPermissions;
}
public void setPermissions(int permissions) {
this.mPermissions = permissions;
}
public long getSharedDate() {
return mSharedDate;
}
public void setSharedDate(long sharedDate) {
this.mSharedDate = sharedDate;
}
public long getExpirationDate() {
return mExpirationDate;
}
public void setExpirationDate(long expirationDate) {
this.mExpirationDate = expirationDate;
}
public String getToken() {
return mToken;
}
public void setToken(String token) {
this.mToken = (token != null) ? token : "";
}
public String getSharedWithDisplayName() {
return mSharedWithDisplayName;
}
public void setSharedWithDisplayName(String sharedWithDisplayName) {
this.mSharedWithDisplayName = (sharedWithDisplayName != null) ? sharedWithDisplayName : "";
}
public String getName() {
return mName;
}
public void setName(String name) {
mName = (name != null) ? name : "";
}
public boolean isFolder() {
return mIsFolder;
}
public void setIsFolder(boolean isFolder) {
this.mIsFolder = isFolder;
}
public long getUserId() {
return mUserId;
}
public void setUserId(long userId) {
this.mUserId = userId;
}
public long getRemoteId() {
return mRemoteId;
}
public void setIdRemoteShared(long remoteId) {
this.mRemoteId = remoteId;
}
public String getShareLink() {
return this.mShareLink;
}
public void setShareLink(String shareLink) {
this.mShareLink = (shareLink != null) ? shareLink : "";
}
public boolean isPasswordProtected() {
return ShareType.PUBLIC_LINK.equals(mShareType) && mShareWith.length() > 0;
}
/**
* Parcelable Methods
*/
public static final Parcelable.Creator<OCShare> CREATOR = new Parcelable.Creator<OCShare>() {
@Override
public OCShare createFromParcel(Parcel source) {
return new OCShare(source);
}
@Override
public OCShare[] newArray(int size) {
return new OCShare[size];
}
};
/**
* Reconstruct from parcel
*
* @param source The source parcel
*/
protected OCShare(Parcel source) {
readFromParcel(source);
}
public void readFromParcel(Parcel source) {
mId = source.readLong();
mFileSource = source.readLong();
mItemSource = source.readLong();
try {
mShareType = ShareType.valueOf(source.readString());
} catch (IllegalArgumentException x) {
mShareType = ShareType.NO_SHARED;
}
mShareWith = source.readString();
mPath = source.readString();
mPermissions = source.readInt();
mSharedDate = source.readLong();
mExpirationDate = source.readLong();
mToken = source.readString();
mSharedWithDisplayName = source.readString();
mIsFolder = source.readInt() == 0;
mUserId = source.readLong();
mRemoteId = source.readLong();
mShareLink = source.readString();
mName = source.readString();
}
@Override
public int describeContents() {
return this.hashCode();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mId);
dest.writeLong(mFileSource);
dest.writeLong(mItemSource);
dest.writeString((mShareType == null) ? "" : mShareType.name());
dest.writeString(mShareWith);
dest.writeString(mPath);
dest.writeInt(mPermissions);
dest.writeLong(mSharedDate);
dest.writeLong(mExpirationDate);
dest.writeString(mToken);
dest.writeString(mSharedWithDisplayName);
dest.writeInt(mIsFolder ? 1 : 0);
dest.writeLong(mUserId);
dest.writeLong(mRemoteId);
dest.writeString(mShareLink);
dest.writeString(mName);
}
}
@@ -0,0 +1,108 @@
/* ownCloud Android Library is available under MIT license
* @author masensio
* @author David A. Velasco
* @author David González Verdugo
* Copyright (C) 2018 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.DeleteMethod;
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;
/**
* Remove a share
*
* @author masensio
* @author David A. Velasco
* @author David González Verdugo
*/
public class RemoveRemoteShareOperation extends RemoteOperation {
private static final String TAG = RemoveRemoteShareOperation.class.getSimpleName();
private int mRemoteShareId;
/**
* Constructor
*
* @param remoteShareId Share ID
*/
public RemoveRemoteShareOperation(int remoteShareId) {
mRemoteShareId = remoteShareId;
}
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result;
try {
Uri requestUri = client.getBaseUri();
Uri.Builder uriBuilder = requestUri.buildUpon();
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH);
uriBuilder.appendEncodedPath(String.valueOf(mRemoteShareId));
DeleteMethod deleteMethod = new DeleteMethod(
new URL(uriBuilder.build().toString())
);
deleteMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE);
int status = client.executeHttpMethod(deleteMethod);
if (isSuccess(status)) {
// Parse xml response and obtain the list of shares
ShareToRemoteOperationResultParser parser = new ShareToRemoteOperationResultParser(
new ShareXMLParser()
);
result = parser.parse(deleteMethod.getResponseBodyAsString());
Log_OC.d(TAG, "Unshare " + mRemoteShareId + ": " + result.getLogMessage());
} else {
result = new RemoteOperationResult<>(deleteMethod);
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Unshare Link Exception " + result.getLogMessage(), e);
}
return result;
}
private boolean isSuccess(int status) {
return (status == HttpConstants.HTTP_OK);
}
}
@@ -0,0 +1,46 @@
/* ownCloud Android Library is available under MIT license
* @author Christian Schabesberger
* Copyright (C) 2018 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 java.util.ArrayList;
public class ShareParserResult {
private ArrayList<OCShare> shares;
private String parserMessage;
public ShareParserResult(ArrayList<OCShare> shares, String parserMessage) {
this.shares = shares;
this.parserMessage = parserMessage;
}
public ArrayList<OCShare> getShares() {
return shares;
}
public String getParserMessage() {
return parserMessage;
}
}
@@ -0,0 +1,106 @@
/* ownCloud Android Library is available under MIT license
* @author David A. Velasco
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.shares;
/**
* Provides method to define a set of share permissions and calculate the appropiate
* int value representing it.
*/
public class SharePermissionsBuilder {
/** Set of permissions */
private int mPermissions = OCShare.READ_PERMISSION_FLAG; // READ is minimum permission
/**
* Sets or clears permission to reshare a file or folder.
*
* @param enabled 'True' to set, 'false' to clear.
* @return Instance to builder itself, to allow consecutive calls to setters
*/
public SharePermissionsBuilder setSharePermission(boolean enabled) {
updatePermission(OCShare.SHARE_PERMISSION_FLAG, enabled);
return this;
}
/**
* Sets or clears permission to update a folder or folder.
*
* @param enabled 'True' to set, 'false' to clear.
* @return Instance to builder itself, to allow consecutive calls to setters
*/
public SharePermissionsBuilder setUpdatePermission(boolean enabled) {
updatePermission(OCShare.UPDATE_PERMISSION_FLAG, enabled);
return this;
}
/**
* Sets or clears permission to create files in share folder.
*
* @param enabled 'True' to set, 'false' to clear.
* @return Instance to builder itself, to allow consecutive calls to setters
*/
public SharePermissionsBuilder setCreatePermission(boolean enabled) {
updatePermission(OCShare.CREATE_PERMISSION_FLAG, enabled);
return this;
}
/**
* Sets or clears permission to delete files in a shared folder.
*
* @param enabled 'True' to set, 'false' to clear.
* @return Instance to builder itself, to allow consecutive calls to setters
*/
public SharePermissionsBuilder setDeletePermission(boolean enabled) {
updatePermission(OCShare.DELETE_PERMISSION_FLAG, enabled);
return this;
}
/**
* Common code to update the value of the set of permissions.
*
* @param permissionsFlag Flag for the permission to update.
* @param enable 'True' to set, 'false' to clear.
*/
private void updatePermission(int permissionsFlag, boolean enable) {
if (enable) {
// add permission
mPermissions |= permissionsFlag;
} else {
// delete permission
mPermissions &= ~permissionsFlag;
}
}
/**
* 'Builds' the int value for the accumulated set of permissions.
*
* @return An int value representing the accumulated set of permissions.
*/
public int build() {
return mPermissions;
}
}
@@ -0,0 +1,142 @@
/* ownCloud Android Library is available under MIT license
* @author David A. Velasco
* @author David González Verdugo
* @author Christian Schabesberger
* Copyright (C) 2018 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.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class ShareToRemoteOperationResultParser {
private static final String TAG = ShareToRemoteOperationResultParser.class.getSimpleName();
private ShareXMLParser mShareXmlParser = null;
private boolean mOneOrMoreSharesRequired = false;
private OwnCloudVersion mOwnCloudVersion = null;
private Uri mServerBaseUri = null;
public ShareToRemoteOperationResultParser(ShareXMLParser shareXmlParser) {
mShareXmlParser = shareXmlParser;
}
public void setOneOrMoreSharesRequired(boolean oneOrMoreSharesRequired) {
mOneOrMoreSharesRequired = oneOrMoreSharesRequired;
}
public void setOwnCloudVersion(OwnCloudVersion ownCloudVersion) {
mOwnCloudVersion = ownCloudVersion;
}
public void setServerBaseUri(Uri serverBaseURi) {
mServerBaseUri = serverBaseURi;
}
public RemoteOperationResult<ShareParserResult> parse(String serverResponse) {
if (serverResponse == null || serverResponse.length() == 0) {
return new RemoteOperationResult<>(RemoteOperationResult.ResultCode.WRONG_SERVER_RESPONSE);
}
RemoteOperationResult<ShareParserResult> result;
final ArrayList<OCShare> resultData = new ArrayList<>();
try {
// Parse xml response and obtain the list of shares
InputStream is = new ByteArrayInputStream(serverResponse.getBytes());
if (mShareXmlParser == null) {
Log_OC.w(TAG, "No ShareXmlParser provided, creating new instance ");
mShareXmlParser = new ShareXMLParser();
}
List<OCShare> shares = mShareXmlParser.parseXMLResponse(is);
if (mShareXmlParser.isSuccess()) {
if ((shares != null && shares.size() > 0) || !mOneOrMoreSharesRequired) {
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.OK);
if (shares != null) {
for (OCShare share : shares) {
resultData.add(share);
// build the share link if not in the response
// (needed for OC servers < 9.0.0, see ShareXMLParser.java#line256)
if (share.getShareType() == ShareType.PUBLIC_LINK
&& (share.getShareLink() == null
|| share.getShareLink().length() <= 0)
&& share.getToken().length() > 0) {
if (mServerBaseUri != null) {
String sharingLinkPath = ShareUtils.getSharingLinkPath(mOwnCloudVersion);
share.setShareLink(mServerBaseUri + sharingLinkPath + share.getToken());
} else {
Log_OC.e(TAG, "Couldn't build link for public share :(");
}
}
}
}
result.setData(new ShareParserResult(resultData, ""));
} else {
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.WRONG_SERVER_RESPONSE);
Log_OC.e(TAG, "Successful status with no share in the response");
}
} else if (mShareXmlParser.isWrongParameter()){
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.SHARE_WRONG_PARAMETER);
result.setData(new ShareParserResult(null, mShareXmlParser.getMessage()));
} else if (mShareXmlParser.isNotFound()){
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND);
result.setData(new ShareParserResult(null, mShareXmlParser.getMessage()));
} else if (mShareXmlParser.isForbidden()) {
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.SHARE_FORBIDDEN);
result.setData(new ShareParserResult(null, mShareXmlParser.getMessage()));
} else {
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.WRONG_SERVER_RESPONSE);
}
} catch (XmlPullParserException e) {
Log_OC.e(TAG, "Error parsing response from server ", e);
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.WRONG_SERVER_RESPONSE);
} catch (IOException e) {
Log_OC.e(TAG, "Error reading response from server ", e);
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.WRONG_SERVER_RESPONSE);
}
return result;
}
}
@@ -0,0 +1,81 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.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
*
* @author masensio
*
*/
public enum ShareType {
NO_SHARED (-1),
USER (0),
GROUP (1),
PUBLIC_LINK (3),
EMAIL (4),
CONTACT (5),
FEDERATED (6);
private int value;
private ShareType(int value)
{
this.value = value;
}
public int getValue() {
return value;
}
public static ShareType fromValue(int value)
{
switch (value)
{
case -1:
return NO_SHARED;
case 0:
return USER;
case 1:
return GROUP;
case 3:
return PUBLIC_LINK;
case 4:
return EMAIL;
case 5:
return CONTACT;
case 6:
return FEDERATED;
}
return null;
}
};
@@ -0,0 +1,53 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 com.owncloud.android.lib.resources.status.OwnCloudVersion;
/**
* Contains Constants for Share Operation
*
* @author masensio
* @author David González Verdugo
*
*/
public class ShareUtils {
// OCS Route
public static final String SHARING_API_PATH ="ocs/v2.php/apps/files_sharing/api/v1/shares";
// String to build the link with the token of a share:
public static final String SHARING_LINK_PATH_BEFORE_VERSION_8 = "/public.php?service=files&t=";
public static final String SHARING_LINK_PATH_AFTER_VERSION_8 = "/index.php/s/";
public static String getSharingLinkPath(OwnCloudVersion version){
if (version!= null && version.isAfter8Version()){
return SHARING_LINK_PATH_AFTER_VERSION_8;
} else {
return SHARING_LINK_PATH_BEFORE_VERSION_8;
}
}
}
@@ -0,0 +1,440 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 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 java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
//import android.util.Log;
import android.util.Xml;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.resources.files.FileUtils;
/**
* Parser for Share API Response
* @author masensio
* @author David González Verdugo
*/
public class ShareXMLParser {
//private static final String TAG = ShareXMLParser.class.getSimpleName();
// No namespaces
private static final String ns = null;
// NODES for XML Parser
private static final String NODE_OCS = "ocs";
private static final String NODE_META = "meta";
private static final String NODE_STATUS = "status";
private static final String NODE_STATUS_CODE = "statuscode";
private static final String NODE_MESSAGE = "message";
private static final String NODE_DATA = "data";
private static final String NODE_ELEMENT = "element";
private static final String NODE_ID = "id";
private static final String NODE_ITEM_TYPE = "item_type";
private static final String NODE_ITEM_SOURCE = "item_source";
private static final String NODE_PARENT = "parent";
private static final String NODE_SHARE_TYPE = "share_type";
private static final String NODE_SHARE_WITH = "share_with";
private static final String NODE_FILE_SOURCE = "file_source";
private static final String NODE_PATH = "path";
private static final String NODE_PERMISSIONS = "permissions";
private static final String NODE_STIME = "stime";
private static final String NODE_EXPIRATION = "expiration";
private static final String NODE_TOKEN = "token";
private static final String NODE_STORAGE = "storage";
private static final String NODE_MAIL_SEND = "mail_send";
private static final String NODE_SHARE_WITH_DISPLAY_NAME = "share_with_displayname";
private static final String NODE_NAME = "name";
private static final String NODE_URL = "url";
private static final String TYPE_FOLDER = "folder";
private static final int SUCCESS = 200;
private static final int ERROR_WRONG_PARAMETER = 400;
private static final int ERROR_FORBIDDEN = 403;
private static final int ERROR_NOT_FOUND = 404;
private String mStatus;
private int mStatusCode;
private String mMessage;
// Getters and Setters
public String getStatus() {
return mStatus;
}
public void setStatus(String status) {
this.mStatus = status;
}
public int getStatusCode() {
return mStatusCode;
}
public void setStatusCode(int statusCode) {
this.mStatusCode = statusCode;
}
public String getMessage() {
return mMessage;
}
public void setMessage(String message) {
this.mMessage = message;
}
// Constructor
public ShareXMLParser() {
mStatusCode = -1;
}
public boolean isSuccess() {
return mStatusCode == SUCCESS;
}
public boolean isForbidden() {
return mStatusCode == ERROR_FORBIDDEN;
}
public boolean isNotFound() {
return mStatusCode == ERROR_NOT_FOUND;
}
public boolean isWrongParameter() {
return mStatusCode == ERROR_WRONG_PARAMETER;
}
/**
* Parse is as response of Share API
* @param is
* @return List of ShareRemoteFiles
* @throws XmlPullParserException
* @throws IOException
*/
public ArrayList<OCShare> parseXMLResponse(InputStream is) throws XmlPullParserException,
IOException {
try {
// XMLPullParser
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(is, null);
parser.nextTag();
return readOCS(parser);
} finally {
is.close();
}
}
/**
* Parse OCS node
* @param parser
* @return List of ShareRemoteFiles
* @throws XmlPullParserException
* @throws IOException
*/
private ArrayList<OCShare> readOCS (XmlPullParser parser) throws XmlPullParserException,
IOException {
ArrayList<OCShare> shares = new ArrayList<>();
parser.require(XmlPullParser.START_TAG, ns , NODE_OCS);
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
// read NODE_META and NODE_DATA
if (name.equalsIgnoreCase(NODE_META)) {
readMeta(parser);
} else if (name.equalsIgnoreCase(NODE_DATA)) {
shares = readData(parser);
} else {
skip(parser);
}
}
return shares;
}
/**
* Parse Meta node
* @param parser
* @throws XmlPullParserException
* @throws IOException
*/
private void readMeta(XmlPullParser parser) throws XmlPullParserException, IOException {
parser.require(XmlPullParser.START_TAG, ns, NODE_META);
//Log_OC.d(TAG, "---- NODE META ---");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if (name.equalsIgnoreCase(NODE_STATUS)) {
setStatus(readNode(parser, NODE_STATUS));
} else if (name.equalsIgnoreCase(NODE_STATUS_CODE)) {
setStatusCode(Integer.parseInt(readNode(parser, NODE_STATUS_CODE)));
} else if (name.equalsIgnoreCase(NODE_MESSAGE)) {
setMessage(readNode(parser, NODE_MESSAGE));
} else {
skip(parser);
}
}
}
/**
* Parse Data node
* @param parser
* @return
* @throws XmlPullParserException
* @throws IOException
*/
private ArrayList<OCShare> readData(XmlPullParser parser) throws XmlPullParserException,
IOException {
ArrayList<OCShare> shares = new ArrayList<OCShare>();
OCShare share = null;
parser.require(XmlPullParser.START_TAG, ns, NODE_DATA);
//Log_OC.d(TAG, "---- NODE DATA ---");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if (name.equalsIgnoreCase(NODE_ELEMENT)) {
readElement(parser, shares);
} else if (name.equalsIgnoreCase(NODE_ID)) {// Parse Create XML Response
share = new OCShare();
String value = readNode(parser, NODE_ID);
share.setIdRemoteShared(Integer.parseInt(value));
} else if (name.equalsIgnoreCase(NODE_URL)) {
// NOTE: this field is received in all the public shares from OC 9.0.0
// in previous versions, it's received in the result of POST requests, but not
// in GET requests
share.setShareType(ShareType.PUBLIC_LINK);
String value = readNode(parser, NODE_URL);
share.setShareLink(value);
} else if (name.equalsIgnoreCase(NODE_TOKEN)) {
share.setToken(readNode(parser, NODE_TOKEN));
} else {
skip(parser);
}
}
if (share != null) {
// this is the response of a request for creation; don't pass to isValidShare()
shares.add(share);
}
return shares;
}
/**
* Parse Element node
* @param parser
* @return
* @throws XmlPullParserException
* @throws IOException
*/
private void readElement(XmlPullParser parser, ArrayList<OCShare> shares)
throws XmlPullParserException, IOException {
parser.require(XmlPullParser.START_TAG, ns, NODE_ELEMENT);
OCShare share = new OCShare();
//Log_OC.d(TAG, "---- NODE ELEMENT ---");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if (name.equalsIgnoreCase(NODE_ELEMENT)) {
// patch to work around servers responding with extra <element> surrounding all
// the shares on the same file before
// https://github.com/owncloud/core/issues/6992 was fixed
readElement(parser, shares);
} else if (name.equalsIgnoreCase(NODE_ID)) {
share.setIdRemoteShared(Integer.parseInt(readNode(parser, NODE_ID)));
} else if (name.equalsIgnoreCase(NODE_ITEM_TYPE)) {
share.setIsFolder(readNode(parser, NODE_ITEM_TYPE).equalsIgnoreCase(TYPE_FOLDER));
fixPathForFolder(share);
} else if (name.equalsIgnoreCase(NODE_ITEM_SOURCE)) {
share.setItemSource(Long.parseLong(readNode(parser, NODE_ITEM_SOURCE)));
} else if (name.equalsIgnoreCase(NODE_PARENT)) {
readNode(parser, NODE_PARENT);
} else if (name.equalsIgnoreCase(NODE_SHARE_TYPE)) {
int value = Integer.parseInt(readNode(parser, NODE_SHARE_TYPE));
share.setShareType(ShareType.fromValue(value));
} else if (name.equalsIgnoreCase(NODE_SHARE_WITH)) {
share.setShareWith(readNode(parser, NODE_SHARE_WITH));
} else if (name.equalsIgnoreCase(NODE_FILE_SOURCE)) {
share.setFileSource(Long.parseLong(readNode(parser, NODE_FILE_SOURCE)));
} else if (name.equalsIgnoreCase(NODE_PATH)) {
share.setPath(readNode(parser, NODE_PATH));
fixPathForFolder(share);
} else if (name.equalsIgnoreCase(NODE_PERMISSIONS)) {
share.setPermissions(Integer.parseInt(readNode(parser, NODE_PERMISSIONS)));
} else if (name.equalsIgnoreCase(NODE_STIME)) {
share.setSharedDate(Long.parseLong(readNode(parser, NODE_STIME)));
} else if (name.equalsIgnoreCase(NODE_EXPIRATION)) {
String value = readNode(parser, NODE_EXPIRATION);
if (!(value.length() == 0)) {
share.setExpirationDate(WebdavUtils.parseResponseDate(value).getTime());
}
} else if (name.equalsIgnoreCase(NODE_TOKEN)) {
share.setToken(readNode(parser, NODE_TOKEN));
} else if (name.equalsIgnoreCase(NODE_STORAGE)) {
readNode(parser, NODE_STORAGE);
} else if (name.equalsIgnoreCase(NODE_MAIL_SEND)) {
readNode(parser, NODE_MAIL_SEND);
} else if (name.equalsIgnoreCase(NODE_SHARE_WITH_DISPLAY_NAME)) {
share.setSharedWithDisplayName(readNode(parser, NODE_SHARE_WITH_DISPLAY_NAME));
} else if (name.equalsIgnoreCase(NODE_URL)) {
String value = readNode(parser, NODE_URL);
share.setShareLink(value);
} else if (name.equalsIgnoreCase(NODE_NAME)) {
share.setName(readNode(parser, NODE_NAME));
} else {
skip(parser);
}
}
if (isValidShare(share)) {
shares.add(share);
}
}
private boolean isValidShare(OCShare share) {
return (share.getRemoteId() > -1);
}
private void fixPathForFolder(OCShare share) {
if (share.isFolder() && share.getPath() != null && share.getPath().length() > 0 &&
!share.getPath().endsWith(FileUtils.PATH_SEPARATOR)) {
share.setPath(share.getPath() + FileUtils.PATH_SEPARATOR);
}
}
/**
* Parse a node, to obtain its text. Needs readText method
* @param parser
* @param node
* @return Text of the node
* @throws XmlPullParserException
* @throws IOException
*/
private String readNode (XmlPullParser parser, String node) throws XmlPullParserException,
IOException{
parser.require(XmlPullParser.START_TAG, ns, node);
String value = readText(parser);
//Log_OC.d(TAG, "node= " + node + ", value= " + value);
parser.require(XmlPullParser.END_TAG, ns, node);
return value;
}
/**
* Read the text from a node
* @param parser
* @return Text of the node
* @throws IOException
* @throws XmlPullParserException
*/
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
String result = "";
if (parser.next() == XmlPullParser.TEXT) {
result = parser.getText();
parser.nextTag();
}
return result;
}
/**
* Skip tags in parser procedure
* @param parser
* @throws XmlPullParserException
* @throws IOException
*/
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
if (parser.getEventType() != XmlPullParser.START_TAG) {
throw new IllegalStateException();
}
int depth = 1;
while (depth != 0) {
switch (parser.next()) {
case XmlPullParser.END_TAG:
depth--;
break;
case XmlPullParser.START_TAG:
depth++;
break;
}
}
}
}
@@ -0,0 +1,239 @@
/* ownCloud Android Library is available under MIT license
*
* Copyright (C) 2018 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.PutMethod;
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;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import okhttp3.FormBody;
/**
* Updates parameters of an existing Share resource, known its remote ID.
*
* Allow updating several parameters, triggering a request to the server per parameter.
*
* @author David A. Velasco
* @author David González Verdugo
*/
public class UpdateRemoteShareOperation extends RemoteOperation<ShareParserResult> {
private static final String TAG = GetRemoteShareOperation.class.getSimpleName();
private static final String PARAM_NAME = "name";
private static final String PARAM_PASSWORD = "password";
private static final String PARAM_EXPIRATION_DATE = "expireDate";
private static final String PARAM_PERMISSIONS = "permissions";
private static final String PARAM_PUBLIC_UPLOAD = "publicUpload";
private static final String FORMAT_EXPIRATION_DATE = "yyyy-MM-dd";
private static final String ENTITY_CONTENT_TYPE = "application/x-www-form-urlencoded";
private static final String ENTITY_CHARSET = "UTF-8";
/**
* Identifier of the share to update
*/
private long mRemoteId;
/**
* Password to set for the public link
*/
private String mPassword;
/**
* Expiration date to set for the public link
*/
private long mExpirationDateInMillis;
/**
* Access permissions for the file bound to the share
*/
private int mPermissions;
/**
* Upload permissions for the public link (only folders)
*/
private Boolean mPublicUpload;
private String mName;
/**
* Constructor. No update is initialized by default, need to be applied with setters below.
*
* @param remoteId Identifier of the share to update.
*/
public UpdateRemoteShareOperation(long remoteId) {
mRemoteId = remoteId;
mPassword = null; // no update
mExpirationDateInMillis = 0; // no update
mPublicUpload = null;
mPermissions = OCShare.DEFAULT_PERMISSION;
}
/**
* Set name to update in Share resource. Ignored by servers previous to version 10.0.0
*
* @param name Name to set to the target share.
* Empty string clears the current name.
* Null results in no update applied to the name.
*/
public void setName(String name) {
this.mName = name;
}
/**
* Set password to update in Share resource.
*
* @param password Password to set to the target share.
* Empty string clears the current password.
* Null results in no update applied to the password.
*/
public void setPassword(String password) {
mPassword = password;
}
/**
* Set expiration date to update in Share resource.
*
* @param expirationDateInMillis Expiration date to set to the target share.
* A negative value clears the current expiration date.
* Zero value (start-of-epoch) results in no update done on
* the expiration date.
*/
public void setExpirationDate(long expirationDateInMillis) {
mExpirationDateInMillis = expirationDateInMillis;
}
/**
* Set permissions to update in Share resource.
*
* @param permissions Permissions to set to the target share.
* Values <= 0 result in no update applied to the permissions.
*/
public void setPermissions(int permissions) {
mPermissions = permissions;
}
/**
* Enable upload permissions to update in Share resource.
*
* @param publicUpload Upload permission to set to the target share.
* Null results in no update applied to the upload permission.
*/
public void setPublicUpload(Boolean publicUpload) {
mPublicUpload = publicUpload;
}
@Override
protected RemoteOperationResult<ShareParserResult> run(OwnCloudClient client) {
RemoteOperationResult<ShareParserResult> result;
try {
FormBody.Builder formBodyBuilder = new FormBody.Builder();
// Parameters to update
if (mName != null) {
formBodyBuilder.add(PARAM_NAME, mName);
}
if (mExpirationDateInMillis < 0) {
// clear expiration date
formBodyBuilder.add(PARAM_EXPIRATION_DATE, "");
} else if (mExpirationDateInMillis > 0) {
// set expiration date
DateFormat dateFormat = new SimpleDateFormat(FORMAT_EXPIRATION_DATE, Locale.GERMAN);
Calendar expirationDate = Calendar.getInstance();
expirationDate.setTimeInMillis(mExpirationDateInMillis);
String formattedExpirationDate = dateFormat.format(expirationDate.getTime());
formBodyBuilder.add(PARAM_EXPIRATION_DATE, formattedExpirationDate);
} // else, ignore - no update
if (mPublicUpload != null) {
formBodyBuilder.add(PARAM_PUBLIC_UPLOAD, Boolean.toString(mPublicUpload));
}
// IMPORTANT: permissions parameter needs to be updated after mPublicUpload parameter,
// otherwise they would be set always as 1 (READ) in the server when mPublicUpload was updated
if (mPermissions > 0) {
// set permissions
formBodyBuilder.add(PARAM_PERMISSIONS, Integer.toString(mPermissions));
}
Uri requestUri = client.getBaseUri();
Uri.Builder uriBuilder = requestUri.buildUpon();
uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH);
uriBuilder.appendEncodedPath(Long.toString(mRemoteId));
PutMethod putMethod = new PutMethod(new URL(uriBuilder.build().toString()));
putMethod.setRequestBody(formBodyBuilder.build());
putMethod.setRequestHeader(HttpConstants.CONTENT_TYPE_HEADER, HttpConstants.CONTENT_TYPE_URLENCODED_UTF8);
putMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE);
int status = client.executeHttpMethod(putMethod);
if (isSuccess(status)) {
// Parse xml response
ShareToRemoteOperationResultParser parser = new ShareToRemoteOperationResultParser(
new ShareXMLParser()
);
parser.setOwnCloudVersion(client.getOwnCloudVersion());
parser.setServerBaseUri(client.getBaseUri());
result = parser.parse(putMethod.getResponseBodyAsString());
} else {
result = new RemoteOperationResult<>(putMethod);
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Exception while Creating New Share", e);
}
return result;
}
private boolean isSuccess(int status) {
return (status == HttpConstants.HTTP_OK);
}
}
@@ -0,0 +1,83 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 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 OCCapability parameters, with values:
* -1 - Unknown
* 0 - False
* 1 - True
*/
public enum CapabilityBooleanType {
UNKNOWN (-1),
FALSE (0),
TRUE (1);
private int value;
CapabilityBooleanType(int value)
{
this.value = value;
}
public int getValue() {
return value;
}
public static CapabilityBooleanType fromValue(int value)
{
switch (value)
{
case -1:
return UNKNOWN;
case 0:
return FALSE;
case 1:
return TRUE;
}
return null;
}
public static CapabilityBooleanType fromBooleanValue(boolean boolValue){
if (boolValue){
return TRUE;
} else {
return FALSE;
}
}
public boolean isUnknown(){
return getValue() == -1;
}
public boolean isFalse(){
return getValue() == 0;
}
public boolean isTrue(){
return getValue() == 1;
}
};
@@ -0,0 +1,288 @@
/* ownCloud Android Library is available under MIT license
* @author masensio
* @author Semih Serhat Karakaya <karakayasemi@itu.edu.tr>
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.status;
import 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 org.json.JSONObject;
import java.net.URL;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Get the Capabilities from the server
* Save in Result.getData in a OCCapability object
*
* @author masensio
* @author David González Verdugo
*/
public class GetRemoteCapabilitiesOperation extends RemoteOperation<OCCapability> {
private static final String TAG = GetRemoteCapabilitiesOperation.class.getSimpleName();
// OCS Routes
private static final String OCS_ROUTE = "ocs/v2.php/cloud/capabilities";
// Arguments - names
private static final String PARAM_FORMAT = "format";
// Arguments - constant values
private static final String VALUE_FORMAT = "json";
// JSON Node names
private static final String NODE_OCS = "ocs";
private static final String NODE_META = "meta";
private static final String NODE_DATA = "data";
private static final String NODE_VERSION = "version";
private static final String NODE_CAPABILITIES = "capabilities";
private static final String NODE_CORE = "core";
private static final String NODE_FILES_SHARING = "files_sharing";
private static final String NODE_PUBLIC = "public";
private static final String NODE_PASSWORD = "password";
private static final String NODE_EXPIRE_DATE = "expire_date";
private static final String NODE_USER = "user";
private static final String NODE_FEDERATION = "federation";
private static final String NODE_FILES = "files";
private static final String PROPERTY_STATUS = "status";
private static final String PROPERTY_STATUSCODE = "statuscode";
private static final String PROPERTY_MESSAGE = "message";
private static final String PROPERTY_POLLINTERVAL = "pollinterval";
private static final String PROPERTY_MAJOR = "major";
private static final String PROPERTY_MINOR = "minor";
private static final String PROPERTY_MICRO = "micro";
private static final String PROPERTY_STRING = "string";
private static final String PROPERTY_EDITION = "edition";
private static final String PROPERTY_API_ENABLED = "api_enabled";
private static final String PROPERTY_ENABLED = "enabled";
private static final String PROPERTY_ENFORCED = "enforced";
private static final String PROPERTY_DAYS = "days";
private static final String PROPERTY_SEND_MAIL = "send_mail";
private static final String PROPERTY_UPLOAD = "upload";
private static final String PROPERTY_UPLOAD_ONLY = "supports_upload_only";
private static final String PROPERTY_MULTIPLE = "multiple";
private static final String PROPERTY_RESHARING = "resharing";
private static final String PROPERTY_OUTGOING = "outgoing";
private static final String PROPERTY_INCOMING = "incoming";
private static final String PROPERTY_BIGFILECHUNKING = "bigfilechunking";
private static final String PROPERTY_UNDELETE = "undelete";
private static final String PROPERTY_VERSIONING = "versioning";
/**
* Constructor
*
*/
public GetRemoteCapabilitiesOperation() {
}
@Override
protected RemoteOperationResult<OCCapability> run(OwnCloudClient client) {
RemoteOperationResult<OCCapability> result;
try {
Uri requestUri = client.getBaseUri();
Uri.Builder uriBuilder = requestUri.buildUpon();
uriBuilder.appendEncodedPath(OCS_ROUTE); // avoid starting "/" in this method
uriBuilder.appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT);
GetMethod getMethod = new GetMethod(new URL(uriBuilder.build().toString()));
getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE);
int status = client.executeHttpMethod(getMethod);
String response = getMethod.getResponseBodyAsString();
if(isSuccess(status)) {
Log_OC.d(TAG, "Successful response: " + response);
// Parse the response
JSONObject respJSON = new JSONObject(response);
JSONObject respOCS = respJSON.getJSONObject(NODE_OCS);
JSONObject respMeta = respOCS.getJSONObject(NODE_META);
JSONObject respData = respOCS.getJSONObject(NODE_DATA);
// Read meta
boolean statusProp = respMeta.getString(PROPERTY_STATUS).equalsIgnoreCase("ok");
int statuscode = respMeta.getInt(PROPERTY_STATUSCODE);
String message = respMeta.getString(PROPERTY_MESSAGE);
if (statusProp) {
OCCapability capability = new OCCapability();
// Add Version
if (respData.has(NODE_VERSION)) {
JSONObject respVersion = respData.getJSONObject(NODE_VERSION);
capability.setVersionMayor(respVersion.getInt(PROPERTY_MAJOR));
capability.setVersionMinor(respVersion.getInt(PROPERTY_MINOR));
capability.setVersionMicro(respVersion.getInt(PROPERTY_MICRO));
capability.setVersionString(respVersion.getString(PROPERTY_STRING));
capability.setVersionEdition(respVersion.getString(PROPERTY_EDITION));
Log_OC.d(TAG, "*** Added " + NODE_VERSION);
}
// Capabilities Object
if (respData.has(NODE_CAPABILITIES)) {
JSONObject respCapabilities = respData.getJSONObject(NODE_CAPABILITIES);
// Add Core: pollinterval
if (respCapabilities.has(NODE_CORE)) {
JSONObject respCore = respCapabilities.getJSONObject(NODE_CORE);
capability.setCorePollinterval(respCore.getInt(PROPERTY_POLLINTERVAL));
Log_OC.d(TAG, "*** Added " + NODE_CORE);
}
// Add files_sharing: public, user, resharing
if (respCapabilities.has(NODE_FILES_SHARING)) {
JSONObject respFilesSharing = respCapabilities.getJSONObject(NODE_FILES_SHARING);
if (respFilesSharing.has(PROPERTY_API_ENABLED)) {
capability.setFilesSharingApiEnabled(CapabilityBooleanType.fromBooleanValue(
respFilesSharing.getBoolean(PROPERTY_API_ENABLED)));
}
if (respFilesSharing.has(NODE_PUBLIC)) {
JSONObject respPublic = respFilesSharing.getJSONObject(NODE_PUBLIC);
capability.setFilesSharingPublicEnabled(CapabilityBooleanType.fromBooleanValue(
respPublic.getBoolean(PROPERTY_ENABLED)));
if(respPublic.has(NODE_PASSWORD)) {
capability.setFilesSharingPublicPasswordEnforced(
CapabilityBooleanType.fromBooleanValue(
respPublic.getJSONObject(NODE_PASSWORD).getBoolean(PROPERTY_ENFORCED)));
}
if(respPublic.has(NODE_EXPIRE_DATE)){
JSONObject respExpireDate = respPublic.getJSONObject(NODE_EXPIRE_DATE);
capability.setFilesSharingPublicExpireDateEnabled(
CapabilityBooleanType.fromBooleanValue(
respExpireDate.getBoolean(PROPERTY_ENABLED)));
if (respExpireDate.has(PROPERTY_DAYS)) {
capability.setFilesSharingPublicExpireDateDays(
respExpireDate.getInt(PROPERTY_DAYS));
}
if (respExpireDate.has(PROPERTY_ENFORCED)) {
capability.setFilesSharingPublicExpireDateEnforced(
CapabilityBooleanType.fromBooleanValue(
respExpireDate.getBoolean(PROPERTY_ENFORCED)));
}
}
if (respPublic.has(PROPERTY_UPLOAD)){
capability.setFilesSharingPublicUpload(CapabilityBooleanType.fromBooleanValue(
respPublic.getBoolean(PROPERTY_UPLOAD)));
}
if (respPublic.has(PROPERTY_UPLOAD_ONLY)) {
capability.setFilesSharingPublicSupportsUploadOnly(CapabilityBooleanType.fromBooleanValue(
respPublic.getBoolean(PROPERTY_UPLOAD_ONLY)));
}
if (respPublic.has(PROPERTY_MULTIPLE)) {
capability.setFilesSharingPublicMultiple(CapabilityBooleanType.fromBooleanValue(
respPublic.getBoolean(PROPERTY_MULTIPLE)));
}
}
if (respFilesSharing.has(NODE_USER)) {
JSONObject respUser = respFilesSharing.getJSONObject(NODE_USER);
capability.setFilesSharingUserSendMail(CapabilityBooleanType.fromBooleanValue(
respUser.getBoolean(PROPERTY_SEND_MAIL)));
}
capability.setFilesSharingResharing(CapabilityBooleanType.fromBooleanValue(
respFilesSharing.getBoolean(PROPERTY_RESHARING)));
if (respFilesSharing.has(NODE_FEDERATION)) {
JSONObject respFederation = respFilesSharing.getJSONObject(NODE_FEDERATION);
capability.setFilesSharingFederationOutgoing(
CapabilityBooleanType.fromBooleanValue(respFederation.getBoolean(PROPERTY_OUTGOING)));
capability.setFilesSharingFederationIncoming(CapabilityBooleanType.fromBooleanValue(
respFederation.getBoolean(PROPERTY_INCOMING)));
}
Log_OC.d(TAG, "*** Added " + NODE_FILES_SHARING);
}
if (respCapabilities.has(NODE_FILES)) {
JSONObject respFiles = respCapabilities.getJSONObject(NODE_FILES);
// Add files
capability.setFilesBigFileChuncking(CapabilityBooleanType.fromBooleanValue(
respFiles.getBoolean(PROPERTY_BIGFILECHUNKING)));
if (respFiles.has(PROPERTY_UNDELETE)) {
capability.setFilesUndelete(CapabilityBooleanType.fromBooleanValue(
respFiles.getBoolean(PROPERTY_UNDELETE)));
}
if (respFiles.has(PROPERTY_VERSIONING)) {
capability.setFilesVersioning(CapabilityBooleanType.fromBooleanValue(
respFiles.getBoolean(PROPERTY_VERSIONING)));
}
Log_OC.d(TAG, "*** Added " + NODE_FILES);
}
}
// Result
result = new RemoteOperationResult<>(OK);
result.setData(capability);
Log_OC.d(TAG, "*** Get Capabilities completed ");
} else {
result = new RemoteOperationResult<>(statuscode, message, null);
Log_OC.e(TAG, "Failed response while getting capabilities from the server ");
Log_OC.e(TAG, "*** status: " + statusProp + "; message: " + message);
}
} else {
result = new RemoteOperationResult<>(getMethod);
Log_OC.e(TAG, "Failed response while getting capabilities from the server ");
if (response != null) {
Log_OC.e(TAG, "*** status code: " + status + "; response message: " + response);
} else {
Log_OC.e(TAG, "*** status code: " + status);
}
}
} catch (Exception e) {
result = new RemoteOperationResult<>(e);
Log_OC.e(TAG, "Exception while getting capabilities", e);
}
return result;
}
private boolean isSuccess(int status) {
return (status == HttpConstants.HTTP_OK);
}
}
@@ -0,0 +1,201 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.resources.status;
import android.content.Context;
import android.net.ConnectivityManager;
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 org.json.JSONException;
import org.json.JSONObject;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLException;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Checks if the server is valid and if the server supports the Share API
*
* @author David A. Velasco
* @author masensio
* @author David González Verdugo
*/
public class GetRemoteStatusOperation extends RemoteOperation<OwnCloudVersion> {
/**
* Maximum time to wait for a response from the server when the connection is being tested,
* in MILLISECONDs.
*/
public static final long TRY_CONNECTION_TIMEOUT = 5000;
private static final String TAG = GetRemoteStatusOperation.class.getSimpleName();
private static final String NODE_INSTALLED = "installed";
private static final String NODE_VERSION = "version";
private static final String HTTPS_PREFIX = "https://";
private static final String HTTP_PREFIX = "http://";
private RemoteOperationResult<OwnCloudVersion> mLatestResult;
private Context mContext;
public GetRemoteStatusOperation(Context context) {
mContext = context;
}
private boolean tryConnection(OwnCloudClient client) {
boolean retval = false;
String baseUrlSt = client.getBaseUri().toString();
try {
GetMethod getMethod = new GetMethod(new URL(baseUrlSt + OwnCloudClient.STATUS_PATH));
getMethod.setReadTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
getMethod.setConnectionTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
client.setFollowRedirects(false);
boolean isRedirectToNonSecureConnection = false;
int status;
try {
status = client.executeHttpMethod(getMethod);
mLatestResult = isSuccess(status)
? new RemoteOperationResult<>(OK)
: new RemoteOperationResult<>(getMethod);
} catch (SSLException sslE) {
mLatestResult = new RemoteOperationResult(sslE);
return false;
}
String redirectedLocation = mLatestResult.getRedirectedLocation();
while (redirectedLocation != null && redirectedLocation.length() > 0
&& !mLatestResult.isSuccess()) {
isRedirectToNonSecureConnection |= (
baseUrlSt.startsWith(HTTPS_PREFIX) &&
redirectedLocation.startsWith(HTTP_PREFIX)
);
getMethod = new GetMethod(new URL(redirectedLocation));
getMethod.setReadTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
getMethod.setConnectionTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
status = client.executeHttpMethod(getMethod);
mLatestResult = new RemoteOperationResult<>(getMethod);
redirectedLocation = mLatestResult.getRedirectedLocation();
}
if (isSuccess(status)) {
JSONObject respJSON = new JSONObject(getMethod.getResponseBodyAsString());
if (!respJSON.getBoolean(NODE_INSTALLED)) {
mLatestResult = new RemoteOperationResult(
RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
} else {
String version = respJSON.getString(NODE_VERSION);
OwnCloudVersion ocVersion = new OwnCloudVersion(version);
/// the version object will be returned even if the version is invalid, no error code;
/// every app will decide how to act if (ocVersion.isVersionValid() == false)
if (isRedirectToNonSecureConnection) {
mLatestResult = new RemoteOperationResult<>(
RemoteOperationResult.ResultCode.
OK_REDIRECT_TO_NON_SECURE_CONNECTION);
} else {
mLatestResult = new RemoteOperationResult<>(
baseUrlSt.startsWith(HTTPS_PREFIX) ?
RemoteOperationResult.ResultCode.OK_SSL :
RemoteOperationResult.ResultCode.OK_NO_SSL);
}
mLatestResult.setData(ocVersion);
retval = true;
}
} else {
mLatestResult = new RemoteOperationResult<>(getMethod);
}
} catch (JSONException e) {
mLatestResult = new RemoteOperationResult<>(
RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
} catch (Exception e) {
mLatestResult = new RemoteOperationResult<>(e);
}
if (mLatestResult.isSuccess()) {
Log_OC.i(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage());
} else if (mLatestResult.getException() != null) {
Log_OC.e(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage(),
mLatestResult.getException());
} else {
Log_OC.e(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage());
}
return retval;
}
private boolean isOnline() {
ConnectivityManager cm = (ConnectivityManager) mContext
.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm != null && cm.getActiveNetworkInfo() != null
&& cm.getActiveNetworkInfo().isConnectedOrConnecting();
}
@Override
protected RemoteOperationResult<OwnCloudVersion> run(OwnCloudClient client) {
if (!isOnline()) {
return new RemoteOperationResult<>(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
}
String baseUriStr = client.getBaseUri().toString();
if (baseUriStr.startsWith(HTTP_PREFIX) || baseUriStr.startsWith(HTTPS_PREFIX)) {
tryConnection(client);
} else {
client.setBaseUri(Uri.parse(HTTPS_PREFIX + baseUriStr));
boolean httpsSuccess = tryConnection(client);
if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) {
Log_OC.d(TAG, "establishing secure connection failed, trying non secure connection");
client.setBaseUri(Uri.parse(HTTP_PREFIX + baseUriStr));
tryConnection(client);
}
}
return mLatestResult;
}
private boolean isSuccess(int status) {
return (status == HttpConstants.HTTP_OK);
}
}

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