diff --git a/.travis.yml b/.travis.yml index 9f906ec0..2920d050 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,10 @@ language: java jdk: oraclejdk7 before_install: - sudo apt-get update -qq -- sudo apt-get install -qq libstdc++6:i386 lib32z1 -- curl -3L https://raw.github.com/embarkmobile/android-sdk-installer/version-1/android-sdk-installer - | bash /dev/stdin --install=$COMPONENTS +- sudo apt-get install -qq libstdc++6:i386 lib32z1 expect +- export LICENSES="android-sdk-license-5be876d5|android-sdk-license-598b93a6" +- curl -3L https://raw.github.com/embarkmobile/android-sdk-installer/version-2/android-sdk-installer + | bash /dev/stdin --install=$COMPONENTS --accept=$LICENSES - source ~/.android-sdk-installer/env - echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI -c 20M - emulator -avd test -no-skin -no-audio -no-window & @@ -24,4 +25,4 @@ env: - secure: aF4U20Xlu/rfrbxCmoJAiGh1doYTAZ10UEDmajuinT+ZGSJLivuqD7DDY/00sI6IXWg+J1vL+7jJm4JSYusHPg38UHZ4q92k6RmZycW2ATUzZnGT54O5FRnY67MfVwgVpIMK9UOL/6NEciBHEjlIOL0wbKQiJB++1YtBZOQLGL4= - secure: N+ECSwNg8v2GsAFJ2y/tCiffauHDpN76zuFI2pDqf0fjmCtJZHu4BH5ArXBHjyHKmgn20a/8eZXcwJaH1HsJ80bo7vDJ2miShjGIQ90hPcdmUiB2XVJcew4f04CtvMDH5o7DRt4ykWArlbPL2rhVag0jotlSidolHBwRFnbDhDY= matrix: - - COMPONENTS=build-tools-19.0.3,android-19,sysimg-19 ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a + - COMPONENTS=build-tools-20.0.0,android-19,sys-img-armeabi-v7a-android-19 ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 708f7893..eaa33e5e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -33,3 +33,4 @@ android:targetSdkVersion="19" /> + diff --git a/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java b/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java index 7383dc0a..8e24c15e 100644 --- a/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java +++ b/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java @@ -36,6 +36,7 @@ import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; import com.owncloud.android.lib.resources.files.RemoteFile; import com.owncloud.android.lib.common.operations.RemoteOperation; @@ -80,7 +81,12 @@ public class MainActivity extends Activity implements OnRemoteOperationListener, Uri serverUri = Uri.parse(getString(R.string.server_base_url) + AccountUtils.WEBDAV_PATH_4_0); mClient = OwnCloudClientFactory.createOwnCloudClient(serverUri, this, true); - mClient.setBasicCredentials(getString(R.string.username), getString(R.string.password)); + mClient.setCredentials( + OwnCloudCredentialsFactory.newBasicCredentials( + getString(R.string.username), + getString(R.string.password) + ) + ); mFilesAdapter = new FilesArrayAdapter(this, R.layout.file_in_list); ((ListView)findViewById(R.id.list_view)).setAdapter(mFilesAdapter); diff --git a/src/com/owncloud/android/lib/common/OwnCloudAccount.java b/src/com/owncloud/android/lib/common/OwnCloudAccount.java new file mode 100644 index 00000000..339eb128 --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudAccount.java @@ -0,0 +1,98 @@ +/* ownCloud Android client application + * Copyright (C) 2014 ownCloud Inc. + * + * 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 . + * + */ + +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 android.accounts.Account; +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 mSavedAccountName; + + + public OwnCloudAccount(Account savedAccount, Context context) + throws AccountNotFoundException, AuthenticatorException, + IOException, OperationCanceledException { + + if (savedAccount == null) { + throw new IllegalArgumentException("Parameter 'savedAccount' cannot be null"); + } + if (context == null) { + throw new IllegalArgumentException("Parameter 'context' cannot be null"); + } + + mSavedAccountName = savedAccount.name; + mBaseUri = Uri.parse(AccountUtils.getBaseUrlForAccount(context, savedAccount)); + mCredentials = AccountUtils.getCredentialsForAccount(context, savedAccount); + if (mCredentials == null) { + mCredentials = OwnCloudCredentialsFactory.getAnonymousCredentials(); + } + } + + + public OwnCloudAccount(Uri baseUri, OwnCloudCredentials credentials) { + if (baseUri == null) { + throw new IllegalArgumentException("Parameter 'baseUri' cannot be null"); + } + mSavedAccountName = null; + mBaseUri = baseUri; + mCredentials = credentials != null ? + credentials : OwnCloudCredentialsFactory.getAnonymousCredentials(); + String username = mCredentials.getUsername(); + if (username != null) { + mSavedAccountName = AccountUtils.buildAccountName(mBaseUri, username); + } + } + + + public boolean isAnonymous() { + return (mCredentials == null); + } + + public Uri getBaseUri() { + return mBaseUri; + } + + public OwnCloudCredentials getCredentials() { + return mCredentials; + } + + public String getName() { + return mSavedAccountName; + } + + +} \ No newline at end of file diff --git a/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java new file mode 100644 index 00000000..6855cfc4 --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java @@ -0,0 +1,48 @@ +package com.owncloud.android.lib.common; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.AuthPolicy; +import org.apache.commons.httpclient.auth.AuthScope; + +public class OwnCloudBasicCredentials implements OwnCloudCredentials { + + private String mUsername; + private String mPassword; + + public OwnCloudBasicCredentials(String username, String password) { + mUsername = username != null ? username : ""; + mPassword = password != null ? password : ""; + } + + @Override + public void applyTo(OwnCloudClient client) { + List authPrefs = new ArrayList(1); + authPrefs.add(AuthPolicy.BASIC); + client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); + + client.getParams().setAuthenticationPreemptive(true); + client.getState().setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(mUsername, mPassword) + ); + } + + @Override + public String getUsername() { + return mUsername; + } + + @Override + public String getAuthToken() { + return mPassword; + } + + @Override + public boolean authTokenExpires() { + return false; + } + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java new file mode 100644 index 00000000..fb6f2018 --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java @@ -0,0 +1,51 @@ +package com.owncloud.android.lib.common; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.httpclient.auth.AuthPolicy; +import org.apache.commons.httpclient.auth.AuthScope; + +import com.owncloud.android.lib.common.network.BearerAuthScheme; +import com.owncloud.android.lib.common.network.BearerCredentials; + +public class OwnCloudBearerCredentials implements OwnCloudCredentials { + + private String mAccessToken; + + public OwnCloudBearerCredentials(String accessToken) { + mAccessToken = accessToken != null ? accessToken : ""; + } + + @Override + public void applyTo(OwnCloudClient client) { + AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class); + + List authPrefs = new ArrayList(1); + authPrefs.add(BearerAuthScheme.AUTH_POLICY); + client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); + + client.getParams().setAuthenticationPreemptive(true); + client.getState().setCredentials( + AuthScope.ANY, + new BearerCredentials(mAccessToken) + ); + } + + @Override + public String getUsername() { + // its unknown + return null; + } + + @Override + public String getAuthToken() { + return mAccessToken; + } + + @Override + public boolean authTokenExpires() { + return true; + } + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudClient.java b/src/com/owncloud/android/lib/common/OwnCloudClient.java index 14405326..7b48bbc0 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/src/com/owncloud/android/lib/common/OwnCloudClient.java @@ -27,10 +27,8 @@ package com.owncloud.android.lib.common; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpConnectionManager; @@ -39,92 +37,96 @@ import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpMethodBase; import org.apache.commons.httpclient.HttpVersion; import org.apache.commons.httpclient.URI; -import org.apache.commons.httpclient.UsernamePasswordCredentials; -import org.apache.commons.httpclient.auth.AuthPolicy; -import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.cookie.CookiePolicy; import org.apache.commons.httpclient.methods.HeadMethod; import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.http.HttpStatus; import org.apache.http.params.CoreProtocolPNames; -import com.owncloud.android.lib.common.network.BearerAuthScheme; -import com.owncloud.android.lib.common.network.BearerCredentials; -import com.owncloud.android.lib.common.network.WebdavUtils; - - import android.net.Uri; import android.util.Log; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials; +import com.owncloud.android.lib.common.accounts.AccountUtils; +import com.owncloud.android.lib.common.network.WebdavUtils; + public class OwnCloudClient extends HttpClient { - private static final int MAX_REDIRECTIONS_COUNT = 3; - - private Uri mUri; - private Uri mWebdavUri; - private Credentials mCredentials; - private boolean mFollowRedirects; - private String mSsoSessionCookie; - final private static String TAG = OwnCloudClient.class.getSimpleName(); + + private static final String TAG = OwnCloudClient.class.getSimpleName(); public static final String USER_AGENT = "Android-ownCloud"; + private static final int MAX_REDIRECTIONS_COUNT = 3; + private static final String PARAM_SINGLE_COOKIE_HEADER = "http.protocol.single-cookie-header"; + private static final boolean PARAM_SINGLE_COOKIE_HEADER_VALUE = true; - static private byte[] sExhaustBuffer = new byte[1024]; + private static byte[] sExhaustBuffer = new byte[1024]; + + private static int sIntanceCounter = 0; + private boolean mFollowRedirects = true; + private OwnCloudCredentials mCredentials = null; + private int mInstanceNumber = 0; + + private Uri mBaseUri; /** * Constructor */ - public OwnCloudClient(HttpConnectionManager connectionMgr) { + public OwnCloudClient(Uri baseUri, HttpConnectionManager connectionMgr) { super(connectionMgr); - Log.d(TAG, "Creating OwnCloudClient"); + + if (baseUri == null) { + throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL"); + } + mBaseUri = baseUri; + + mInstanceNumber = sIntanceCounter++; + Log.d(TAG + " #" + mInstanceNumber, "Creating OwnCloudClient"); + getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT); - getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1); - mFollowRedirects = true; - mSsoSessionCookie = null; + getParams().setParameter( + CoreProtocolPNames.PROTOCOL_VERSION, + HttpVersion.HTTP_1_1); + + getParams().setCookiePolicy( + CookiePolicy.IGNORE_COOKIES); + getParams().setParameter( + PARAM_SINGLE_COOKIE_HEADER, // to avoid problems with some web servers + PARAM_SINGLE_COOKIE_HEADER_VALUE); + + clearCredentials(); } - public void setBearerCredentials(String accessToken) { - AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class); - - List authPrefs = new ArrayList(1); - authPrefs.add(BearerAuthScheme.AUTH_POLICY); - getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); - - mCredentials = new BearerCredentials(accessToken); - getState().setCredentials(AuthScope.ANY, mCredentials); - mSsoSessionCookie = null; - } - - public void setBasicCredentials(String username, String password) { - List authPrefs = new ArrayList(1); - authPrefs.add(AuthPolicy.BASIC); - getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); - - getParams().setAuthenticationPreemptive(true); - mCredentials = new UsernamePasswordCredentials(username, password); - getState().setCredentials(AuthScope.ANY, mCredentials); - mSsoSessionCookie = null; - } - - public void setSsoSessionCookie(String accessToken) { - getParams().setAuthenticationPreemptive(false); - getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES); - mSsoSessionCookie = accessToken; - mCredentials = null; + + 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); + } /** * Check if a file exists in the OC server * - * TODO replace with ExistenceOperation + * @deprecated Use ExistenceCheckOperation instead * - * @return 'true' if the file exists; 'false' it doesn't exist - * @throws Exception When the existence could not be determined + * @return 'true' if the file exists; 'false' it doesn't exist + * @throws Exception When the existence could not be determined */ + @Deprecated public boolean existsFile(String path) throws IOException, HttpException { - HeadMethod head = new HeadMethod(mWebdavUri.toString() + WebdavUtils.encodePath(path)); + HeadMethod head = new HeadMethod(getWebdavUri() + WebdavUtils.encodePath(path)); try { int status = executeMethod(head); - Log.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + ((status != HttpStatus.SC_OK)?"(FAIL)":"")); + Log.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + + ((status != HttpStatus.SC_OK)?"(FAIL)":"")); exhaustResponse(head.getResponseBodyAsStream()); return (status == HttpStatus.SC_OK); @@ -140,19 +142,21 @@ public class OwnCloudClient extends HttpClient { * * Sets the socket and connection timeouts only for the method received. * - * The timeouts are both in milliseconds; 0 means 'infinite'; < 0 means 'do not change the default' + * The timeouts are both in milliseconds; 0 means 'infinite'; + * < 0 means 'do not change the default' * * @param method HTTP method request. * @param readTimeout Timeout to set for data reception * @param conntionTimout Timeout to set for connection establishment */ - public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) throws HttpException, IOException { + public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) + throws HttpException, IOException { int oldSoTimeout = getParams().getSoTimeout(); int oldConnectionTimeout = getHttpConnectionManager().getParams().getConnectionTimeout(); try { if (readTimeout >= 0) { method.getParams().setSoTimeout(readTimeout); // this should be enough... - getParams().setSoTimeout(readTimeout); // ... but this looks like necessary for HTTPS + getParams().setSoTimeout(readTimeout); // ... but HTTPS needs this } if (connectionTimeout >= 0) { getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout); @@ -167,43 +171,72 @@ public class OwnCloudClient extends HttpClient { @Override public int executeMethod(HttpMethod method) throws IOException, HttpException { - boolean customRedirectionNeeded = false; - try { - method.setFollowRedirects(mFollowRedirects); - } catch (Exception e) { - //if (mFollowRedirects) Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + " method, custom redirection will be used if needed"); - customRedirectionNeeded = mFollowRedirects; + try { // just to log + boolean customRedirectionNeeded = false; + + try { + method.setFollowRedirects(mFollowRedirects); + } catch (Exception e) { + /* + if (mFollowRedirects) + Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + + " method, custom redirection will be used if needed"); + */ + customRedirectionNeeded = mFollowRedirects; + } + + Log.d(TAG + " #" + mInstanceNumber, "REQUEST " + + method.getName() + " " + method.getPath()); + +// logCookiesAtRequest(method.getRequestHeaders(), "before"); +// logCookiesAtState("before"); + + int status = super.executeMethod(method); + + if (customRedirectionNeeded) { + status = patchRedirection(status, method); + } + +// logCookiesAtRequest(method.getRequestHeaders(), "after"); +// logCookiesAtState("after"); +// logSetCookiesAtResponse(method.getResponseHeaders()); + + return status; + + } catch (IOException e) { + Log.d(TAG + " #" + mInstanceNumber, "Exception occured", e); + throw e; } - if (mSsoSessionCookie != null && mSsoSessionCookie.length() > 0) { - method.addRequestHeader("Cookie", mSsoSessionCookie); - } - int status = super.executeMethod(method); + } + + private int patchRedirection(int status, HttpMethod method) throws HttpException, IOException { int redirectionsCount = 0; - while (customRedirectionNeeded && - redirectionsCount < MAX_REDIRECTIONS_COUNT && + while (redirectionsCount < MAX_REDIRECTIONS_COUNT && ( status == HttpStatus.SC_MOVED_PERMANENTLY || status == HttpStatus.SC_MOVED_TEMPORARILY || status == HttpStatus.SC_TEMPORARY_REDIRECT) ) { Header location = method.getResponseHeader("Location"); + if (location == null) { + location = method.getResponseHeader("location"); + } if (location != null) { - Log.d(TAG, "Location to redirect: " + location.getValue()); + Log.d(TAG + " #" + mInstanceNumber, + "Location to redirect: " + location.getValue()); method.setURI(new URI(location.getValue(), true)); status = super.executeMethod(method); redirectionsCount++; } else { - Log.d(TAG, "No location to redirect!"); + Log.d(TAG + " #" + mInstanceNumber, "No location to redirect!"); status = HttpStatus.SC_NOT_FOUND; } } - return status; - } + } - - /** + /** * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation. * * @param responseBodyAsStream InputStream with the HTTP response to exhaust. @@ -215,53 +248,137 @@ public class OwnCloudClient extends HttpClient { responseBodyAsStream.close(); } catch (IOException io) { - Log.e(TAG, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED", io); + Log.e(TAG, "Unexpected exception while exhausting not interesting HTTP response;" + + " will be IGNORED", io); } } } /** - * Sets the connection and wait-for-data timeouts to be applied by default to the methods performed by this client. + * Sets the connection and wait-for-data timeouts to be applied by default to the methods + * performed by this client. */ public void setDefaultTimeouts(int defaultDataTimeout, int defaultConnectionTimeout) { - getParams().setSoTimeout(defaultDataTimeout); - getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout); - } - - /** - * Sets the Webdav URI for the helper methods that receive paths as parameters, instead of full URLs - * @param uri - */ - public void setWebdavUri(Uri uri) { - mWebdavUri = uri; + if (defaultDataTimeout >= 0) { + getParams().setSoTimeout(defaultDataTimeout); + } + if (defaultConnectionTimeout >= 0) { + getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout); + } } public Uri getWebdavUri() { - return mWebdavUri; + if (mCredentials instanceof OwnCloudBearerCredentials) { + return Uri.parse(mBaseUri + AccountUtils.ODAV_PATH); + } else { + return Uri.parse(mBaseUri + AccountUtils.WEBDAV_PATH_4_0); + } } /** - * Sets the base URI for the helper methods that receive paths as parameters, instead of full URLs + * Sets the root URI to the ownCloud server. + * + * Use with care. + * * @param uri */ public void setBaseUri(Uri uri) { - mUri = uri; + if (uri == null) { + throw new IllegalArgumentException("URI cannot be NULL"); + } + mBaseUri = uri; } public Uri getBaseUri() { - return mUri; + return mBaseUri; } - public final Credentials getCredentials() { + public final OwnCloudCredentials getCredentials() { return mCredentials; } - public final String getSsoSessionCookie() { - return mSsoSessionCookie; - } - public void setFollowRedirects(boolean followRedirects) { mFollowRedirects = followRedirects; } + + private void logCookiesAtRequest(Header[] headers, String when) { + int counter = 0; + for (int i=0; i future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), null, currentActivity, null, null); + AccountManagerFuture 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.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token + client.setCredentials( + OwnCloudCredentialsFactory.newBearerCredentials(accessToken) + ); } else if (isSamlSso) { // TODO avoid a call to getUserData here - AccountManagerFuture future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), null, currentActivity, null, null); + AccountManagerFuture 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.setSsoSessionCookie(accessToken); + client.setCredentials( + OwnCloudCredentialsFactory.newSamlSsoCredentials(accessToken) + ); + } else { String username = account.name.substring(0, account.name.lastIndexOf('@')); //String password = am.getPassword(account); //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false); - AccountManagerFuture future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypePass(account.type), null, currentActivity, null, null); + AccountManagerFuture future = am.getAuthToken( + account, + AccountTypeUtils.getAuthTokenTypePass(account.type), + null, + currentActivity, + null, + null); + Bundle result = future.getResult(); String password = result.getString(AccountManager.KEY_AUTHTOKEN); - client.setBasicCredentials(username, password); + client.setCredentials( + OwnCloudCredentialsFactory.newBasicCredentials(username, password) + ); } + // 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 + * @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 */ @@ -150,14 +198,12 @@ public class OwnCloudClientFactory { Log.e(TAG, "The local server truststore could not be read. Default SSL management in the system will be used for HTTPS connections", e); } - OwnCloudClient client = new OwnCloudClient(NetworkUtils.getMultiThreadedConnManager()); - + OwnCloudClient client = new OwnCloudClient(uri, NetworkUtils.getMultiThreadedConnManager()); client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT); - client.setWebdavUri(uri); client.setFollowRedirects(followRedirects); return client; } - + } diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientManager.java b/src/com/owncloud/android/lib/common/OwnCloudClientManager.java new file mode 100644 index 00000000..e54ae191 --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudClientManager.java @@ -0,0 +1,53 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +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 + */ + +public interface OwnCloudClientManager { + + public OwnCloudClient getClientFor(OwnCloudAccount account, Context context); + + public OwnCloudClient removeClientFor(OwnCloudAccount account); + + public void saveAllClients(Context context, String accountType) + throws AccountNotFoundException, AuthenticatorException, + IOException, OperationCanceledException; + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java b/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java new file mode 100644 index 00000000..216e2e30 --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java @@ -0,0 +1,67 @@ +package com.owncloud.android.lib.common; + +public class OwnCloudClientManagerFactory { + + public static enum Policy { + ALWAYS_NEW_CLIENT, + SINGLE_SESSION_PER_ACCOUNT + } + + private static Policy sDefaultPolicy = Policy.ALWAYS_NEW_CLIENT; + + private static OwnCloudClientManager sDefaultSingleton; + + 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(); + + 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; + } + + 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; + } + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudCredentials.java new file mode 100644 index 00000000..a3fff2eb --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudCredentials.java @@ -0,0 +1,30 @@ +/* ownCloud Android client application + * Copyright (C) 2014 ownCloud Inc. + * + * 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 . + * + */ + +package com.owncloud.android.lib.common; + +public interface OwnCloudCredentials { + + public void applyTo(OwnCloudClient ownCloudClient); + + public String getUsername(); + + public String getAuthToken(); + + public boolean authTokenExpires(); + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java b/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java new file mode 100644 index 00000000..ee84859d --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java @@ -0,0 +1,54 @@ +package com.owncloud.android.lib.common; + +public class OwnCloudCredentialsFactory { + + private static OwnCloudAnonymousCredentials sAnonymousCredentials; + + public static OwnCloudCredentials newBasicCredentials(String username, String password) { + return new OwnCloudBasicCredentials(username, password); + } + + public static OwnCloudCredentials newBearerCredentials(String authToken) { + return new OwnCloudBearerCredentials(authToken); + } + + public static OwnCloudCredentials newSamlSsoCredentials(String sessionCookie) { + return new OwnCloudSamlSsoCredentials(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) { + client.getState().clearCredentials(); + client.getState().clearCookies(); + } + + @Override + public String getAuthToken() { + return ""; + } + + @Override + public boolean authTokenExpires() { + return false; + } + + @Override + public String getUsername() { + // no user name + return null; + } + } + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java new file mode 100644 index 00000000..d59f5b0c --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java @@ -0,0 +1,57 @@ +package com.owncloud.android.lib.common; + +import org.apache.commons.httpclient.Cookie; +import org.apache.commons.httpclient.cookie.CookiePolicy; + +import android.net.Uri; + +public class OwnCloudSamlSsoCredentials implements OwnCloudCredentials { + + private String mSessionCookie; + + public OwnCloudSamlSsoCredentials(String sessionCookie) { + mSessionCookie = sessionCookie != null ? sessionCookie : ""; + } + + @Override + public void applyTo(OwnCloudClient client) { + client.getParams().setAuthenticationPreemptive(false); + client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); + client.setFollowRedirects(false); + + Uri serverUri = client.getBaseUri(); + + String[] cookies = mSessionCookie.split(";"); + if (cookies.length > 0) { + Cookie cookie = null; + for (int i=0; i= 0) { + cookie = new Cookie(); + cookie.setName(cookies[i].substring(0, equalPos)); + cookie.setValue(cookies[i].substring(equalPos + 1)); + cookie.setDomain(serverUri.getHost()); // VERY IMPORTANT + cookie.setPath(serverUri.getPath()); // VERY IMPORTANT + client.getState().addCookie(cookie); + } + } + } + } + + @Override + public String getUsername() { + // its unknown + return null; + } + + @Override + public String getAuthToken() { + return mSessionCookie; + } + + @Override + public boolean authTokenExpires() { + return true; + } + +} diff --git a/src/com/owncloud/android/lib/common/SimpleFactoryManager.java b/src/com/owncloud/android/lib/common/SimpleFactoryManager.java new file mode 100644 index 00000000..94918ec6 --- /dev/null +++ b/src/com/owncloud/android/lib/common/SimpleFactoryManager.java @@ -0,0 +1,68 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.common; + + +import com.owncloud.android.lib.common.accounts.AccountUtils; + +import android.content.Context; +import android.util.Log; + +public class SimpleFactoryManager implements OwnCloudClientManager { + + private static final String TAG = SimpleFactoryManager.class.getSimpleName(); + + @Override + public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) { + Log.d(TAG, "getClientFor(OwnCloudAccount ... : "); + OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient( + account.getBaseUri(), + context.getApplicationContext(), + true); + + Log.d(TAG, " new client {" + + (account.getName() != null ? + account.getName() : + AccountUtils.buildAccountName( + account.getBaseUri(), + account.getCredentials().getAuthToken())) + + ", " + client.hashCode() + "}"); + + client.setCredentials(account.getCredentials()); + 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! + } + +} diff --git a/src/com/owncloud/android/lib/common/SingleSessionManager.java b/src/com/owncloud/android/lib/common/SingleSessionManager.java new file mode 100644 index 00000000..638362f6 --- /dev/null +++ b/src/com/owncloud/android/lib/common/SingleSessionManager.java @@ -0,0 +1,209 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.common; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.httpclient.cookie.CookiePolicy; + +import android.accounts.Account; +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.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientFactory; +import com.owncloud.android.lib.common.accounts.AccountUtils; +import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; + +/** + * Implementation of {@link OwnCloudClientManager} + * + * TODO check multithreading safety + * + * @author David A. Velasco + * @author masensio + */ + +public class SingleSessionManager implements OwnCloudClientManager { + + private static final String TAG = SingleSessionManager.class.getSimpleName(); + + private Map mClientsWithKnownUsername = + new HashMap(); + + private Map mClientsWithUnknownUsername = + new HashMap(); + + + @Override + public synchronized OwnCloudClient getClientFor(OwnCloudAccount account, Context context) { + Log.d(TAG, "getClientFor(OwnCloudAccount ... : "); + if (account == null) { + throw new IllegalArgumentException("Cannot get an OwnCloudClient for a null account"); + } + + OwnCloudClient client = null; + String accountName = account.getName(); + String sessionName = 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) { + Log.d(TAG, " reusing client {" + sessionName + ", " + + client.hashCode() + "}"); + mClientsWithKnownUsername.put(accountName, client); + Log.d(TAG, " moved client to {" + accountName + ", " + + client.hashCode() + "}"); + } + } else { + client = mClientsWithUnknownUsername.get(sessionName); + } + } else { + Log.d(TAG, " reusing client {" + accountName + ", " + client.hashCode() + "}"); + 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.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); + // enable cookie tracking + + + // Restore Cookies ?? + AccountUtils.restoreCookies(accountName, client, context); + + client.setCredentials(account.getCredentials()); + if (accountName != null) { + mClientsWithKnownUsername.put(accountName, client); + Log.d(TAG, " new client {" + accountName + ", " + client.hashCode() + "}"); + + } else { + mClientsWithUnknownUsername.put(sessionName, client); + Log.d(TAG, " new client {" + sessionName + ", " + client.hashCode() + "}"); + } + } else { + if (!reusingKnown) { + Log.d(TAG, " reusing client {" + sessionName + ", " + client.hashCode() + "}"); + } + keepCredentialsUpdated(account, client); + keepUriUpdated(account, client); + } + + return client; + } + + + @Override + public OwnCloudClient removeClientFor(OwnCloudAccount account) { + + if (account == null) { + return null; + } + + OwnCloudClient client = null; + String accountName = account.getName(); + if (accountName != null) { + client = mClientsWithKnownUsername.remove(accountName); + if (client != null) { + Log.d(TAG, "Removed client {" + accountName + ", " + client.hashCode() + "}"); + return client; + } else { + Log.d(TAG, "No client tracked for {" + accountName + "}"); + } + } + + String sessionName = AccountUtils.buildAccountName( + account.getBaseUri(), + account.getCredentials().getAuthToken()); + client = mClientsWithUnknownUsername.remove(sessionName); + if (client != null) { + Log.d(TAG, "Removed client {" + sessionName + ", " + client.hashCode() + "}"); + return client; + } + Log.d(TAG, "No client tracked for {" + sessionName + "}"); + + Log.d(TAG, "No client removed"); + return null; + + } + + + @Override + public synchronized void saveAllClients(Context context, String accountType) + throws AccountNotFoundException, AuthenticatorException, IOException, + OperationCanceledException { + + Iterator accountNames = mClientsWithKnownUsername.keySet().iterator(); + String accountName = null; + Account account = null; + while (accountNames.hasNext()) { + accountName = accountNames.next(); + account = new Account(accountName, accountType); + AccountUtils.saveClient( + mClientsWithKnownUsername.get(accountName), + account, + context); + } + } + + + private void keepCredentialsUpdated(OwnCloudAccount account, OwnCloudClient reusedClient) { + OwnCloudCredentials recentCredentials = account.getCredentials(); + if (!recentCredentials.getAuthToken().equals( + reusedClient.getCredentials().getAuthToken())) { + reusedClient.setCredentials(recentCredentials); + } + + } + + // 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); + } + + } + + +} diff --git a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java index 7f0cd351..591f93f7 100644 --- a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java +++ b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java @@ -25,18 +25,32 @@ package com.owncloud.android.lib.common.accounts; -import com.owncloud.android.lib.resources.status.OwnCloudVersion; +import java.io.IOException; + +import org.apache.commons.httpclient.Cookie; 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 android.util.Log; + +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudCredentials; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.resources.status.OwnCloudVersion; public class AccountUtils { + + private static final String TAG = AccountUtils.class.getSimpleName(); + public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php"; public static final String WEBDAV_PATH_2_0 = "/files/webdav.php"; public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav"; - private static final String ODAV_PATH = "/remote.php/odav"; + public static final String ODAV_PATH = "/remote.php/odav"; private static final String SAML_SSO_PATH = "/remote.php/webdav"; public static final String CARDDAV_PATH_2_0 = "/apps/contacts/carddav.php"; public static final String CARDDAV_PATH_4_0 = "/remote/carddav.php"; @@ -72,11 +86,15 @@ public class AccountUtils { /** * Constructs full url to host and webdav resource basing on host version + * + * @deprecated To be removed in release 1.0. + * * @param context * @param account * @return url or null on failure * @throws AccountNotFoundException When 'account' is unknown for the AccountManager */ + @Deprecated public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException { AccountManager ama = AccountManager.get(context); String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL); @@ -94,13 +112,32 @@ public class AccountUtils { /** * Extracts url server from the account + * + * @deprecated This method will be removed in version 1.0. + * Use {@link #getBaseUrlForAccount(Context, Account)} + * instead. + * * @param context * @param account * @return url server or null on failure * @throws AccountNotFoundException When 'account' is unknown for the AccountManager */ - public static String constructBasicURLForAccount(Context context, Account account) throws AccountNotFoundException { - AccountManager ama = AccountManager.get(context); + @Deprecated + public static String constructBasicURLForAccount(Context context, Account account) + throws AccountNotFoundException { + return getBaseUrlForAccount(context, account); + } + + /** + * Extracts url server from the account + * @param context + * @param account + * @return url server or null on failure + * @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 ) @@ -109,7 +146,148 @@ public class AccountUtils { return baseurl; } + + /** + * + * @return + * @throws IOException + * @throws AuthenticatorException + * @throws OperationCanceledException + */ + public static OwnCloudCredentials getCredentialsForAccount(Context context, Account account) + throws OperationCanceledException, AuthenticatorException, IOException { + + OwnCloudCredentials credentials = null; + AccountManager am = AccountManager.get(context); + + boolean isOauth2 = am.getUserData( + account, + AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null; + + boolean isSamlSso = am.getUserData( + account, + AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null; + + if (isOauth2) { + String accessToken = am.blockingGetAuthToken( + account, + AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), + false); + + credentials = OwnCloudCredentialsFactory.newBearerCredentials(accessToken); + + } else if (isSamlSso) { + String accessToken = am.blockingGetAuthToken( + account, + AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), + false); + + credentials = OwnCloudCredentialsFactory.newSamlSsoCredentials(accessToken); + + } else { + String username = account.name.substring(0, account.name.lastIndexOf('@')); + String password = am.blockingGetAuthToken( + account, + AccountTypeUtils.getAuthTokenTypePass(account.type), + false); + + credentials = OwnCloudCredentialsFactory.newBasicCredentials(username, password); + } + + return credentials; + + } + + + public static String buildAccountName(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 void saveClient(OwnCloudClient client, Account savedAccount, Context context) { + + // Account Manager + AccountManager ac = AccountManager.get(context.getApplicationContext()); + + if (client != null) { + String cookiesString = client.getCookiesString(); + if (cookiesString != "") { + ac.setUserData(savedAccount, Constants.KEY_COOKIES, cookiesString); + // Log.d(TAG, "Saving Cookies: "+ cookiesString ); + } + } + + } + + + /** + * Restore the client cookies + * @param account + * @param client + * @param context + */ + public static void restoreCookies(Account account, OwnCloudClient client, Context context) { + + Log.d(TAG, "Restoring cookies for " + account.name); + + // Account Manager + AccountManager am = AccountManager.get(context.getApplicationContext()); + + Uri serverUri = (client.getBaseUri() != null)? client.getBaseUri() : client.getWebdavUri(); + + String cookiesString = am.getUserData(account, Constants.KEY_COOKIES); + if (cookiesString !=null) { + String[] cookies = cookiesString.split(";"); + if (cookies.length > 0) { + for (int i=0; i< cookies.length; i++) { + Cookie cookie = new Cookie(); + int equalPos = cookies[i].indexOf('='); + cookie.setName(cookies[i].substring(0, equalPos)); + cookie.setValue(cookies[i].substring(equalPos + 1)); + cookie.setDomain(serverUri.getHost()); // VERY IMPORTANT + cookie.setPath(serverUri.getPath()); // VERY IMPORTANT + + client.getState().addCookie(cookie); + } + } + } + } + + /** + * Restore the client cookies from accountName + * @param accountName + * @param client + * @param context + */ + public static void restoreCookies(String accountName, OwnCloudClient client, Context context) { + Log.d(TAG, "Restoring cookies for " + accountName); + + // Account Manager + AccountManager am = AccountManager.get(context.getApplicationContext()); + + // Get account + Account account = null; + Account accounts[] = am.getAccounts(); + for (Account a : accounts) { + if (a.name.equals(accountName)) { + account = a; + break; + } + } + + // Restoring cookies + if (account != null) { + restoreCookies(account, client, context); + } + } + public static class AccountNotFoundException extends AccountsException { /** Generated - should be refreshed every time the class changes!! */ @@ -160,5 +338,10 @@ public class AccountUtils { * Flag signaling if the ownCloud server supports Share API" */ public static final String KEY_SUPPORTS_SHARE_API = "oc_supports_share_api"; + /** + * OC accout cookies + */ + public static final String KEY_COOKIES = "oc_account_cookies"; } + } diff --git a/src/com/owncloud/android/lib/common/network/WebdavEntry.java b/src/com/owncloud/android/lib/common/network/WebdavEntry.java index b7ac35b5..d6fe7a54 100644 --- a/src/com/owncloud/android/lib/common/network/WebdavEntry.java +++ b/src/com/owncloud/android/lib/common/network/WebdavEntry.java @@ -30,18 +30,20 @@ import org.apache.jackrabbit.webdav.MultiStatusResponse; import org.apache.jackrabbit.webdav.property.DavProperty; import org.apache.jackrabbit.webdav.property.DavPropertyName; import org.apache.jackrabbit.webdav.property.DavPropertySet; - - - +import org.apache.jackrabbit.webdav.xml.Namespace; import android.net.Uri; import android.util.Log; public class WebdavEntry { - private String mName, mPath, mUri, mContentType, mEtag; - private long mContentLength, mCreateTimestamp, mModifiedTimestamp; + private static final String NAMESPACE_OC = "http://owncloud.org/ns"; + private static final String EXTENDED_PROPERTY_NAME_PERMISSIONS = "permissions"; + private static final String EXTENDED_PROPERTY_NAME_REMOTE_ID = "id"; - public WebdavEntry(MultiStatusResponse ms, String splitElement) { + private String mName, mPath, mUri, mContentType, mEtag, mPermissions, mRemoteId; + private long mContentLength, mCreateTimestamp, mModifiedTimestamp; + + public WebdavEntry(MultiStatusResponse ms, String splitElement) { resetData(); if (ms.getStatus().length != 0) { mUri = ms.getHref(); @@ -106,6 +108,22 @@ public class WebdavEntry { mEtag = mEtag.substring(1, mEtag.length()-1); } + // OC permissions property + prop = propSet.get( + EXTENDED_PROPERTY_NAME_PERMISSIONS, Namespace.getNamespace(NAMESPACE_OC) + ); + if (prop != null) { + mPermissions = prop.getValue().toString(); + } + + // OC remote id property + prop = propSet.get( + EXTENDED_PROPERTY_NAME_REMOTE_ID, Namespace.getNamespace(NAMESPACE_OC) + ); + if (prop != null) { + mRemoteId = prop.getValue().toString(); + } + } else { Log.e("WebdavEntry", "General fuckup, no status for webdav response"); @@ -152,8 +170,16 @@ public class WebdavEntry { return mEtag; } + public String permissions() { + return mPermissions; + } + + public String remoteId() { + return mRemoteId; + } + private void resetData() { - mName = mUri = mContentType = null; + mName = mUri = mContentType = mPermissions = null; mRemoteId = null; mContentLength = mCreateTimestamp = mModifiedTimestamp = 0; } } diff --git a/src/com/owncloud/android/lib/common/network/WebdavUtils.java b/src/com/owncloud/android/lib/common/network/WebdavUtils.java index d64042bf..c701e45e 100644 --- a/src/com/owncloud/android/lib/common/network/WebdavUtils.java +++ b/src/com/owncloud/android/lib/common/network/WebdavUtils.java @@ -35,32 +35,30 @@ import android.net.Uri; 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") }; - - public static String prepareXmlForPropFind() { - String ret = ""; - return ret; - } - - public static String prepareXmlForPatch() { - return ""; - } + 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 { - returnDate = DATETIME_FORMATS[i].parse(date); + format = DATETIME_FORMATS[i]; + synchronized(format) { + returnDate = format.parse(date); + } return returnDate; } catch (ParseException e) { + // this is not the format } } return null; diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java index 9d453090..bef4b86c 100644 --- a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java +++ b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java @@ -26,11 +26,12 @@ package com.owncloud.android.lib.common.operations; import java.io.IOException; -import org.apache.commons.httpclient.Credentials; - +import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.network.BearerCredentials; +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; +import com.owncloud.android.lib.common.OwnCloudCredentials; +import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; @@ -105,7 +106,9 @@ public abstract class RemoteOperation implements Runnable { mAccount = account; mContext = context.getApplicationContext(); try { - mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext); + OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); + mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). + getClientFor(ocAccount, mContext); } catch (Exception e) { Log.e(TAG, "Error while trying to access to " + mAccount.name, e); return new RemoteOperationResult(e); @@ -135,12 +138,17 @@ public abstract class RemoteOperation implements Runnable { * * This method should be used whenever an ownCloud account is available, instead of {@link #execute(OwnCloudClient)}. * + * @deprecated This method will be removed in version 1.0. + * Use {@link #execute(Account, Context, OnRemoteOperationListener, Handler)} + * instead. + * * @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. */ + @Deprecated public Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) { if (account == null) throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account"); @@ -161,6 +169,42 @@ public abstract class RemoteOperation implements Runnable { } + /** + * 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 = account; + mContext = context.getApplicationContext(); + mCallerActivity = null; + mClient = null; // the client instance will be created from mAccount and mContext in the runnerThread to create below + + mListener = listener; + + mListenerHandler = listenerHandler; + + Thread runnerThread = new Thread(this); + runnerThread.start(); + return runnerThread; + } + + /** * Asynchronously executes the remote operation * @@ -205,11 +249,17 @@ public abstract class RemoteOperation implements Runnable { try{ if (mClient == null) { if (mAccount != null && mContext != null) { + /** DEPRECATED BLOCK - will be removed at version 1.0 */ if (mCallerActivity != null) { - mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext, mCallerActivity); + mClient = OwnCloudClientFactory.createOwnCloudClient( + mAccount, mContext, mCallerActivity); } else { - mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext); + /** EOF DEPRECATED */ + 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 instance or account"); } @@ -228,21 +278,20 @@ public abstract class RemoteOperation implements Runnable { result = run(mClient); repeat = false; + /** DEPRECATED BLOCK - will be removed at version 1.0 ; don't trust in this code + * to trigger authentication update */ if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() && -// (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) { (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) { /// possible fail due to lack of authorization in an operation performed in foreground - Credentials cred = mClient.getCredentials(); - String ssoSessionCookie = mClient.getSsoSessionCookie(); - if (cred != null || ssoSessionCookie != null) { + OwnCloudCredentials cred = mClient.getCredentials(); + if (cred != null) { /// confirmed : unauthorized operation AccountManager am = AccountManager.get(mContext); - boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials); - boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null); - if (bearerAuthorization) { - am.invalidateAuthToken(mAccount.type, ((BearerCredentials)cred).getAccessToken()); - } else if (samlBasedSsoAuthorization ) { - am.invalidateAuthToken(mAccount.type, ssoSessionCookie); + if (cred.authTokenExpires()) { + am.invalidateAuthToken( + mAccount.type, + cred.getAuthToken() + ); } else { am.clearPassword(mAccount); } @@ -251,8 +300,14 @@ public abstract class RemoteOperation implements Runnable { result = null; } } + /** EOF DEPRECATED BLOCK **/ } while (repeat); + if (mAccount != null && mContext != null) { + // Save Client Cookies + AccountUtils.saveClient(mClient, mAccount, mContext); + } + final RemoteOperationResult resultToSend = result; if (mListenerHandler != null && mListener != null) { mListenerHandler.post(new Runnable() { diff --git a/src/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java b/src/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java index 4a1c33a6..922b7c9f 100644 --- a/src/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java +++ b/src/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java @@ -163,6 +163,8 @@ public class ReadRemoteFolderOperation extends RemoteOperation { file.setMimeType(we.contentType()); file.setModifiedTimestamp(we.modifiedTimestamp()); file.setEtag(we.etag()); + file.setPermissions(we.permissions()); + file.setRemoteId(we.remoteId()); return file; } } diff --git a/src/com/owncloud/android/lib/resources/files/RemoteFile.java b/src/com/owncloud/android/lib/resources/files/RemoteFile.java index 0b96584c..0db4e591 100644 --- a/src/com/owncloud/android/lib/resources/files/RemoteFile.java +++ b/src/com/owncloud/android/lib/resources/files/RemoteFile.java @@ -48,7 +48,9 @@ public class RemoteFile implements Parcelable, Serializable { private long mCreationTimestamp; private long mModifiedTimestamp; private String mEtag; - + private String mPermissions; + private String mRemoteId; + /** * Getters and Setters */ @@ -101,6 +103,22 @@ public class RemoteFile implements Parcelable, Serializable { 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 RemoteFile() { resetData(); } @@ -127,6 +145,8 @@ public class RemoteFile implements Parcelable, Serializable { this.setMimeType(we.contentType()); this.setModifiedTimestamp(we.modifiedTimestamp()); this.setEtag(we.etag()); + this.setPermissions(we.permissions()); + this.setRemoteId(we.remoteId()); } /** @@ -139,6 +159,8 @@ public class RemoteFile implements Parcelable, Serializable { mCreationTimestamp = 0; mModifiedTimestamp = 0; mEtag = null; + mPermissions = null; + mRemoteId = null; } /** @@ -173,6 +195,8 @@ public class RemoteFile implements Parcelable, Serializable { mCreationTimestamp = source.readLong(); mModifiedTimestamp = source.readLong(); mEtag = source.readString(); + mPermissions= source.readString(); + mRemoteId = source.readString(); } @Override @@ -187,7 +211,9 @@ public class RemoteFile implements Parcelable, Serializable { dest.writeLong(mLength); dest.writeLong(mCreationTimestamp); dest.writeLong(mModifiedTimestamp); - dest.writeString(mEtag); + dest.writeString(mEtag); + dest.writeString(mPermissions); + dest.writeString(mRemoteId); } diff --git a/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java b/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java index 9a675587..458a91d3 100644 --- a/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java +++ b/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java @@ -59,37 +59,39 @@ public class GetRemoteStatusOperation extends RemoteOperation { private static final String NODE_INSTALLED = "installed"; private static final String NODE_VERSION = "version"; - private String mUrl; private RemoteOperationResult mLatestResult; private Context mContext; - public GetRemoteStatusOperation(String url, Context context) { - mUrl = url; + public GetRemoteStatusOperation(Context context) { mContext = context; } - private boolean tryConnection(OwnCloudClient wc, String urlSt) { + private boolean tryConnection(OwnCloudClient client) { boolean retval = false; GetMethod get = null; + String baseUrlSt = client.getBaseUri().toString(); try { - get = new GetMethod(urlSt); - int status = wc.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT); + get = new GetMethod(baseUrlSt + AccountUtils.STATUS_PATH); + int status = client.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT); String response = get.getResponseBodyAsString(); if (status == HttpStatus.SC_OK) { JSONObject json = new JSONObject(response); if (!json.getBoolean(NODE_INSTALLED)) { - mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED); + mLatestResult = new RemoteOperationResult( + RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED); } else { String version = json.getString(NODE_VERSION); OwnCloudVersion ocVersion = new OwnCloudVersion(version); if (!ocVersion.isVersionValid()) { - mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION); + mLatestResult = new RemoteOperationResult( + RemoteOperationResult.ResultCode.BAD_OC_VERSION); } else { - mLatestResult = new RemoteOperationResult(urlSt.startsWith("https://") ? - RemoteOperationResult.ResultCode.OK_SSL : - RemoteOperationResult.ResultCode.OK_NO_SSL - ); + mLatestResult = new RemoteOperationResult( + baseUrlSt.startsWith("https://") ? + RemoteOperationResult.ResultCode.OK_SSL : + RemoteOperationResult.ResultCode.OK_NO_SSL + ); ArrayList data = new ArrayList(); data.add(ocVersion); @@ -103,7 +105,8 @@ public class GetRemoteStatusOperation extends RemoteOperation { } } catch (JSONException e) { - mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED); + mLatestResult = new RemoteOperationResult( + RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED); } catch (Exception e) { mLatestResult = new RemoteOperationResult(e); @@ -114,13 +117,13 @@ public class GetRemoteStatusOperation extends RemoteOperation { } if (mLatestResult.isSuccess()) { - Log.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage()); + Log.i(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage()); } else if (mLatestResult.getException() != null) { - Log.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException()); + Log.e(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException()); } else { - Log.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage()); + Log.e(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage()); } return retval; @@ -138,16 +141,17 @@ public class GetRemoteStatusOperation extends RemoteOperation { if (!isOnline()) { return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION); } - if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) { - tryConnection(client, mUrl + AccountUtils.STATUS_PATH); + String baseUriStr = client.getBaseUri().toString(); + if (baseUriStr.startsWith("http://") || baseUriStr.startsWith("https://")) { + tryConnection(client); } else { - client.setWebdavUri(Uri.parse("https://" + mUrl + AccountUtils.STATUS_PATH)); - boolean httpsSuccess = tryConnection(client, "https://" + mUrl + AccountUtils.STATUS_PATH); + client.setBaseUri(Uri.parse("https://" + baseUriStr)); + boolean httpsSuccess = tryConnection(client); if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) { Log.d(TAG, "establishing secure connection failed, trying non secure connection"); - client.setWebdavUri(Uri.parse("http://" + mUrl + AccountUtils.STATUS_PATH)); - tryConnection(client, "http://" + mUrl + AccountUtils.STATUS_PATH); + client.setBaseUri(Uri.parse("http://" + baseUriStr)); + tryConnection(client); } } return mLatestResult; diff --git a/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java b/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java index 050b36d2..f153be44 100644 --- a/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java +++ b/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java @@ -78,7 +78,7 @@ public class GetRemoteUserNameOperation extends RemoteOperation { //Get the user try { - get = new GetMethod(client.getWebdavUri() + OCS_ROUTE); + get = new GetMethod(client.getBaseUri() + OCS_ROUTE); get.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE); status = client.executeMethod(get); if(isSuccess(status)) { diff --git a/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java b/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java index 65771dab..66ff274e 100644 --- a/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java +++ b/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java @@ -35,6 +35,7 @@ import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.resources.files.RemoteFile; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -73,7 +74,6 @@ public class TestActivity extends Activity { private String mPass; private boolean mChunked; - private static final String WEBDAV_PATH = "/remote.php/webdav"; private static final int BUFFER_SIZE = 1024; public static final String ASSETS__TEXT_FILE_NAME = "textFile.txt"; @@ -106,14 +106,17 @@ public class TestActivity extends Activity { } } - Uri uri = Uri.parse(mServerUri + WEBDAV_PATH); - mClient = new OwnCloudClient(NetworkUtils.getMultiThreadedConnManager()); + mClient = new OwnCloudClient(Uri.parse(mServerUri), NetworkUtils.getMultiThreadedConnManager()); mClient.setDefaultTimeouts( OwnCloudClientFactory.DEFAULT_DATA_TIMEOUT, OwnCloudClientFactory.DEFAULT_CONNECTION_TIMEOUT); - mClient.setWebdavUri(uri); mClient.setFollowRedirects(true); - mClient.setBasicCredentials(mUser, mPass); + mClient.setCredentials( + OwnCloudCredentialsFactory.newBasicCredentials( + mUser, + mPass + ) + ); mClient.setBaseUri(Uri.parse(mServerUri)); Log.v(TAG, "onCreate finished, ownCloud client ready"); diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientManagerFactoryTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientManagerFactoryTest.java new file mode 100644 index 00000000..b612744c --- /dev/null +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientManagerFactoryTest.java @@ -0,0 +1,155 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.test_project.test; + +import com.owncloud.android.lib.common.OwnCloudClientManager; +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; +import com.owncloud.android.lib.common.SingleSessionManager; +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy; +import com.owncloud.android.lib.common.SimpleFactoryManager; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +/** + * Unit test for OwnCloudClientManagerFactory + * + * @author David A. Velasco + */ +public class OwnCloudClientManagerFactoryTest extends TestCase { + + @Override + protected void setUp() { + OwnCloudClientManagerFactory.setDefaultPolicy(Policy.ALWAYS_NEW_CLIENT); + } + + public void testGetDefaultPolicy() { + Policy defaultPolicy = OwnCloudClientManagerFactory.getDefaultPolicy(); + assertNotNull("Returned null value", defaultPolicy); + assertTrue("Returned unknown value", + (Policy.ALWAYS_NEW_CLIENT.equals(defaultPolicy) || + (Policy.SINGLE_SESSION_PER_ACCOUNT.equals(defaultPolicy)))); + } + + public void testSetDefaultPolicy() { + OwnCloudClientManagerFactory.setDefaultPolicy(Policy.SINGLE_SESSION_PER_ACCOUNT); + Policy defaultPolicy = OwnCloudClientManagerFactory.getDefaultPolicy(); + assertEquals("SINGLE_SESSION_PER_ACCOUNT not set", + Policy.SINGLE_SESSION_PER_ACCOUNT, defaultPolicy); + + OwnCloudClientManagerFactory.setDefaultPolicy(Policy.ALWAYS_NEW_CLIENT); + defaultPolicy = OwnCloudClientManagerFactory.getDefaultPolicy(); + assertEquals("ALWAYS_NEW_CLIENT not set", Policy.ALWAYS_NEW_CLIENT, defaultPolicy); + + try { + OwnCloudClientManagerFactory.setDefaultPolicy(null); + throw new AssertionFailedError("Accepted NULL parameter"); + + } catch(Exception e) { + assertTrue("Unexpected exception when setting default policy null", + (e instanceof IllegalArgumentException)); + } + defaultPolicy = OwnCloudClientManagerFactory.getDefaultPolicy(); + assertEquals("ALWAYS_NEW_CLIENT changed after setting null", + Policy.ALWAYS_NEW_CLIENT, defaultPolicy); + + } + + + public void testGetDefaultSingleton() { + OwnCloudClientManager mgr = OwnCloudClientManagerFactory.getDefaultSingleton(); + assertNotNull("Returned NULL default singleton", mgr); + assertTrue("Default singleton does not implement default policy", + mgr instanceof SimpleFactoryManager); + + OwnCloudClientManager mgr2 = OwnCloudClientManagerFactory.getDefaultSingleton(); + assertSame("Not singleton", mgr, mgr2); + + OwnCloudClientManagerFactory.setDefaultPolicy(Policy.SINGLE_SESSION_PER_ACCOUNT); + mgr = OwnCloudClientManagerFactory.getDefaultSingleton(); + assertNotNull("Returned NULL default singleton", mgr); + assertTrue("Default singleton does not implement default policy", + mgr instanceof SingleSessionManager); + + mgr2 = OwnCloudClientManagerFactory.getDefaultSingleton(); + assertSame("Not singleton", mgr, mgr2); + } + + + public void testNewDefaultOwnCloudClientManager() { + OwnCloudClientManager mgr = OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager(); + assertNotNull("Returned NULL default manager", mgr); + assertTrue("New manager does not implement default policy", + mgr instanceof SimpleFactoryManager); + assertNotSame("Not new instance", + mgr, OwnCloudClientManagerFactory.getDefaultSingleton()); + assertNotSame("Not new instance", + mgr, OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager()); + + OwnCloudClientManagerFactory.setDefaultPolicy(Policy.SINGLE_SESSION_PER_ACCOUNT); + mgr = OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager(); + assertNotNull("Returned NULL default manager", mgr); + assertTrue("New manager does not implement default policy", + mgr instanceof SingleSessionManager); + assertNotSame("Not new instance", + mgr, OwnCloudClientManagerFactory.getDefaultSingleton()); + assertNotSame("Not new instance", + mgr, OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager()); + + } + + + public void testNewOwnCloudClientManager() { + OwnCloudClientManager mgr = OwnCloudClientManagerFactory. + newOwnCloudClientManager(Policy.ALWAYS_NEW_CLIENT); + + assertNotNull("Returned NULL manager", mgr); + assertTrue("New manager does not implement policy ALWAYS_NEW_CLIENT", + mgr instanceof SimpleFactoryManager); + assertNotSame("Not new instance", + mgr, OwnCloudClientManagerFactory.getDefaultSingleton()); + assertNotSame("Not new instance", + mgr, OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager()); + assertNotSame("Not new instance", + mgr, OwnCloudClientManagerFactory.newOwnCloudClientManager( + Policy.ALWAYS_NEW_CLIENT)); + + + OwnCloudClientManager mgr2 = OwnCloudClientManagerFactory. + newOwnCloudClientManager(Policy.SINGLE_SESSION_PER_ACCOUNT); + + assertNotNull("Returned NULL manager", mgr2); + assertTrue("New manager does not implement policy SINGLE_SESSION_PER_ACCOUNT", + mgr2 instanceof SingleSessionManager); + assertNotSame("Not new instance", + mgr2, OwnCloudClientManagerFactory.getDefaultSingleton()); + assertNotSame("Not new instance", + mgr2, OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager()); + assertNotSame("Not new instance", + mgr2, OwnCloudClientManagerFactory.newOwnCloudClientManager( + Policy.SINGLE_SESSION_PER_ACCOUNT)); + } + + +} diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientTest.java new file mode 100644 index 00000000..43de601a --- /dev/null +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientTest.java @@ -0,0 +1,381 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.test_project.test; + +import java.io.IOException; +import java.io.InputStream; +import java.net.SocketTimeoutException; +import java.security.GeneralSecurityException; +import org.apache.commons.httpclient.ConnectTimeoutException; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.methods.HeadMethod; +import org.apache.commons.httpclient.params.HttpMethodParams; +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; +import org.apache.http.HttpStatus; +import org.apache.jackrabbit.webdav.DavConstants; +import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; + +import junit.framework.AssertionFailedError; + +import android.net.Uri; +import android.test.AndroidTestCase; +import android.util.Log; + +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudCredentials; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.accounts.AccountUtils; +import com.owncloud.android.lib.common.network.NetworkUtils; +import com.owncloud.android.lib.test_project.R; +import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory; + + +/** + * Unit test for OwnCloudClient + * + * @author David A. Velasco + */ +public class OwnCloudClientTest extends AndroidTestCase { + + private static final String TAG = OwnCloudClientTest.class.getSimpleName(); + + private Uri mServerUri; + private String mUsername; + private String mPassword; + + public OwnCloudClientTest() { + super(); + + Protocol pr = Protocol.getProtocol("https"); + if (pr == null || !(pr.getSocketFactory() instanceof SelfSignedConfidentSslSocketFactory)) { + try { + ProtocolSocketFactory psf = new SelfSignedConfidentSslSocketFactory(); + Protocol.registerProtocol( + "https", + new Protocol("https", psf, 443)); + + } catch (GeneralSecurityException e) { + throw new AssertionFailedError( + "Self-signed confident SSL context could not be loaded"); + } + } + } + + + @Override + protected void setUp() throws Exception { + super.setUp(); + mServerUri = Uri.parse(getContext().getString(R.string.server_base_url)); + mUsername = getContext().getString(R.string.username); + mPassword = getContext().getString(R.string.password); + } + + + public void testConstructor() { + try { + new OwnCloudClient(null, NetworkUtils.getMultiThreadedConnManager()); + throw new AssertionFailedError("Accepted NULL parameter"); + + } catch(Exception e) { + assertTrue("Unexpected exception passing NULL baseUri", + (e instanceof IllegalArgumentException)); + } + + try { + new OwnCloudClient(mServerUri, null); + throw new AssertionFailedError("Accepted NULL parameter"); + + } catch(Exception e) { + assertTrue("Unexpected exception passing NULL connectionMgr", + (e instanceof IllegalArgumentException)); + } + + OwnCloudClient client = + new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager()); + assertNotNull("OwnCloudClient instance not built", client); + assertEquals("Wrong user agent", + client.getParams().getParameter(HttpMethodParams.USER_AGENT), + OwnCloudClient.USER_AGENT); + } + + + public void testGetSetCredentials() { + OwnCloudClient client = + new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager()); + + assertNotNull("Returned NULL credentials", client.getCredentials()); + assertEquals("Not instanced without credentials", + client.getCredentials(), OwnCloudCredentialsFactory.getAnonymousCredentials()); + + OwnCloudCredentials credentials = + OwnCloudCredentialsFactory.newBasicCredentials("user", "pass"); + client.setCredentials(credentials); + assertEquals("Basic credentials not set", credentials, client.getCredentials()); + + credentials = OwnCloudCredentialsFactory.newBearerCredentials("bearerToken"); + client.setCredentials(credentials); + assertEquals("Bearer credentials not set", credentials, client.getCredentials()); + + credentials = OwnCloudCredentialsFactory.newSamlSsoCredentials("samlSessionCookie=124"); + client.setCredentials(credentials); + assertEquals("SAML2 session credentials not set", credentials, client.getCredentials()); + + } + + public void testExecuteMethodWithTimeouts() throws HttpException, IOException { + OwnCloudClient client = + new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager()); + int connectionTimeout = client.getConnectionTimeout(); + int readTimeout = client.getDataTimeout(); + + HeadMethod head = new HeadMethod(client.getWebdavUri() + "/"); + try { + client.executeMethod(head, 1, 1000); + throw new AssertionFailedError("Completed HEAD with impossible read timeout"); + + } catch (Exception e) { + Log.e("OwnCloudClientTest", "EXCEPTION", e); + assertTrue("Unexcepted exception " + e.getLocalizedMessage(), + (e instanceof ConnectTimeoutException) || + (e instanceof SocketTimeoutException)); + + } finally { + head.releaseConnection(); + } + + assertEquals("Connection timeout was changed for future requests", + connectionTimeout, client.getConnectionTimeout()); + assertEquals("Read timeout was changed for future requests", + readTimeout, client.getDataTimeout()); + + try { + client.executeMethod(head, 1000, 1); + throw new AssertionFailedError("Completed HEAD with impossible connection timeout"); + + } catch (Exception e) { + Log.e("OwnCloudClientTest", "EXCEPTION", e); + assertTrue("Unexcepted exception " + e.getLocalizedMessage(), + (e instanceof ConnectTimeoutException) || + (e instanceof SocketTimeoutException)); + + } finally { + head.releaseConnection(); + } + + assertEquals("Connection timeout was changed for future requests", + connectionTimeout, client.getConnectionTimeout()); + assertEquals("Read timeout was changed for future requests", + readTimeout, client.getDataTimeout()); + + } + + + public void testExecuteMethod() { + OwnCloudClient client = + new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager()); + HeadMethod head = new HeadMethod(client.getWebdavUri() + "/"); + int status = -1; + try { + status = client.executeMethod(head); + assertTrue("Wrong status code returned: " + status, + status > 99 && status < 600); + + } catch (IOException e) { + Log.e(TAG, "Exception in HEAD method execution", e); + // TODO - make it fail? ; try several times, and make it fail if none + // is right? + + } finally { + head.releaseConnection(); + } + } + + + public void testExhaustResponse() { + OwnCloudClient client = + new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager()); + + PropFindMethod propfind = null; + try { + propfind = new PropFindMethod(client.getWebdavUri() + "/", + DavConstants.PROPFIND_ALL_PROP, + DavConstants.DEPTH_0); + client.executeMethod(propfind); + InputStream responseBody = propfind.getResponseBodyAsStream(); + if (responseBody != null) { + client.exhaustResponse(responseBody); + + try { + int character = responseBody.read(); + assertEquals("Response body was not fully exhausted", + character, -1); // -1 is acceptable + + } catch (IOException e) { + // this is the preferred result + } + + } else { + Log.e(TAG, "Could not test exhaustResponse due to wrong response"); + // TODO - make it fail? ; try several times, and make it fail if none + // is right? + } + + } catch (IOException e) { + Log.e(TAG, "Exception in PROPFIND method execution", e); + // TODO - make it fail? ; try several times, and make it fail if none + // is right? + + } finally { + propfind.releaseConnection(); + } + + client.exhaustResponse(null); // must run with no exception + } + + + public void testGetSetDefaultTimeouts() { + OwnCloudClient client = + new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager()); + + int oldDataTimeout = client.getDataTimeout(); + int oldConnectionTimeout = client.getConnectionTimeout(); + + client.setDefaultTimeouts(oldDataTimeout + 1000, oldConnectionTimeout + 1000); + assertEquals("Data timeout not set", + oldDataTimeout + 1000, client.getDataTimeout()); + assertEquals("Connection timeout not set", + oldConnectionTimeout + 1000, client.getConnectionTimeout()); + + client.setDefaultTimeouts(0, 0); + assertEquals("Zero data timeout not set", + 0, client.getDataTimeout()); + assertEquals("Zero connection timeout not set", + 0, client.getConnectionTimeout()); + + client.setDefaultTimeouts(-1, -1); + assertEquals("Negative data timeout not ignored", + 0, client.getDataTimeout()); + assertEquals("Negative connection timeout not ignored", + 0, client.getConnectionTimeout()); + + client.setDefaultTimeouts(-1, 1000); + assertEquals("Negative data timeout not ignored", + 0, client.getDataTimeout()); + assertEquals("Connection timeout not set", + 1000, client.getConnectionTimeout()); + + client.setDefaultTimeouts(1000, -1); + assertEquals("Data timeout not set", + 1000, client.getDataTimeout()); + assertEquals("Negative connection timeout not ignored", + 1000, client.getConnectionTimeout()); + + } + + + public void testGetWebdavUri() { + OwnCloudClient client = + new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager()); + client.setCredentials(OwnCloudCredentialsFactory.newBearerCredentials("fakeToken")); + Uri webdavUri = client.getWebdavUri(); + assertTrue("WebDAV URI does not point to the right entry point for OAuth2 " + + "authenticated servers", + webdavUri.getPath().endsWith(AccountUtils.ODAV_PATH)); + assertTrue("WebDAV URI is not a subpath of base URI", + webdavUri.getAuthority().equals(mServerUri.getAuthority()) && + webdavUri.getPath().startsWith(mServerUri.getPath())); + + client.setCredentials(OwnCloudCredentialsFactory.newBasicCredentials( + mUsername, mPassword)); + webdavUri = client.getWebdavUri(); + assertTrue("WebDAV URI does not point to the right entry point", + webdavUri.getPath().endsWith(AccountUtils.WEBDAV_PATH_4_0)); + PropFindMethod propfind = null; + try { + propfind = new PropFindMethod(webdavUri + "/", + DavConstants.PROPFIND_ALL_PROP, + DavConstants.DEPTH_0); + int status = client.executeMethod(propfind); + assertEquals("WebDAV request did not work on WebDAV URI", + HttpStatus.SC_MULTI_STATUS, status); + + } catch (IOException e) { + Log.e(TAG, "Exception in PROPFIND method execution", e); + // TODO - make it fail? ; try several times, and make it fail if none + // is right? + + } finally { + propfind.releaseConnection(); + } + + } + + + public void testGetSetBaseUri() { + OwnCloudClient client = + new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager()); + assertEquals("Returned base URI different that URI passed to constructor", + mServerUri, client.getBaseUri()); + + Uri otherUri = Uri.parse("https://whatever.com/basePath/here"); + client.setBaseUri(otherUri); + assertEquals("Returned base URI different that URI passed to constructor", + otherUri, client.getBaseUri()); + + try { + client.setBaseUri(null); + throw new AssertionFailedError("Accepted NULL parameter"); + + } catch(Exception e) { + assertTrue("Unexpected exception passing NULL base URI", + (e instanceof IllegalArgumentException)); + } + } + + + public void testGetCookiesString() { + // TODO implement test body + /*public String getCookiesString(){ + Cookie[] cookies = getState().getCookies(); + String cookiesString =""; + for (Cookie cookie: cookies) { + cookiesString = cookiesString + cookie.toString() + ";"; + + logCookie(cookie); + } + + return cookiesString; + + } + */ + } + + + public void testSetFollowRedirects() { + // TODO - to implement this test we need a redirected server + } + + +} diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/SimpleFactoryManagerTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SimpleFactoryManagerTest.java new file mode 100644 index 00000000..3254c589 --- /dev/null +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SimpleFactoryManagerTest.java @@ -0,0 +1,124 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.test_project.test; + +import java.security.GeneralSecurityException; + +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; + +import android.net.Uri; +import android.test.AndroidTestCase; + +import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.SimpleFactoryManager; +import com.owncloud.android.lib.test_project.R; +import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory; + +import junit.framework.AssertionFailedError; + +/** + * Unit test for SimpleFactoryManager + * + * @author David A. Velasco + */ + +public class SimpleFactoryManagerTest extends AndroidTestCase { + + private SimpleFactoryManager mSFMgr; + + private Uri mServerUri; + private String mUsername; + private OwnCloudAccount mValidAccount; + private OwnCloudAccount mAnonymousAccount; + + public SimpleFactoryManagerTest() { + super(); + + Protocol pr = Protocol.getProtocol("https"); + if (pr == null || !(pr.getSocketFactory() instanceof SelfSignedConfidentSslSocketFactory)) { + try { + ProtocolSocketFactory psf = new SelfSignedConfidentSslSocketFactory(); + Protocol.registerProtocol( + "https", + new Protocol("https", psf, 443)); + + } catch (GeneralSecurityException e) { + throw new AssertionFailedError( + "Self-signed confident SSL context could not be loaded"); + } + } + } + + + @Override + protected void setUp() throws Exception { + super.setUp(); + mSFMgr = new SimpleFactoryManager(); + mServerUri = Uri.parse(getContext().getString(R.string.server_base_url)); + mUsername = getContext().getString(R.string.username); + + mValidAccount = new OwnCloudAccount( + mServerUri, OwnCloudCredentialsFactory.newBasicCredentials( + mUsername, + getContext().getString(R.string.password) + ) + ); + + mAnonymousAccount = new OwnCloudAccount( + mServerUri, OwnCloudCredentialsFactory.getAnonymousCredentials()); + + } + + public void testGetClientFor() { + OwnCloudClient client = mSFMgr.getClientFor(mValidAccount, getContext()); + + assertNotSame("Got same client instances for same account", + client, mSFMgr.getClientFor(mValidAccount, getContext())); + + assertNotSame("Got same client instances for different accounts", + client, mSFMgr.getClientFor(mAnonymousAccount, getContext())); + + // TODO harder tests + } + + public void testRemoveClientFor() { + OwnCloudClient client = mSFMgr.getClientFor(mValidAccount, getContext()); + mSFMgr.removeClientFor(mValidAccount); + assertNotSame("Got same client instance after removing it from manager", + client, mSFMgr.getClientFor(mValidAccount, getContext())); + + // TODO harder tests + } + + + public void testSaveAllClients() { + // TODO implement test; + // or refactor saveAllClients() method out of OwnCloudClientManager to make + // it independent of AccountManager + } + +} diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/SingleSessionManagerTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SingleSessionManagerTest.java new file mode 100644 index 00000000..8e0799ae --- /dev/null +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SingleSessionManagerTest.java @@ -0,0 +1,123 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.test_project.test; + +import java.security.GeneralSecurityException; + +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; + +import android.net.Uri; +import android.test.AndroidTestCase; + +import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.SingleSessionManager; +import com.owncloud.android.lib.test_project.R; +import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory; + +import junit.framework.AssertionFailedError; + +/** + * Unit test for SingleSessionManager + * + * @author David A. Velasco + */ +public class SingleSessionManagerTest extends AndroidTestCase { + + private SingleSessionManager mSSMgr; + + private Uri mServerUri; + private String mUsername; + private OwnCloudAccount mValidAccount; + private OwnCloudAccount mAnonymousAccount; + + public SingleSessionManagerTest() { + super(); + + Protocol pr = Protocol.getProtocol("https"); + if (pr == null || !(pr.getSocketFactory() instanceof SelfSignedConfidentSslSocketFactory)) { + try { + ProtocolSocketFactory psf = new SelfSignedConfidentSslSocketFactory(); + Protocol.registerProtocol( + "https", + new Protocol("https", psf, 443)); + + } catch (GeneralSecurityException e) { + throw new AssertionFailedError( + "Self-signed confident SSL context could not be loaded"); + } + } + } + + + @Override + protected void setUp() throws Exception { + super.setUp(); + mSSMgr = new SingleSessionManager(); + mServerUri = Uri.parse(getContext().getString(R.string.server_base_url)); + mUsername = getContext().getString(R.string.username); + + mValidAccount = new OwnCloudAccount( + mServerUri, OwnCloudCredentialsFactory.newBasicCredentials( + mUsername, + getContext().getString(R.string.password) + ) + ); + + mAnonymousAccount = new OwnCloudAccount( + mServerUri, OwnCloudCredentialsFactory.getAnonymousCredentials()); + + } + + public void testGetClientFor() { + OwnCloudClient client1 = mSSMgr.getClientFor(mValidAccount, getContext()); + OwnCloudClient client2 = mSSMgr.getClientFor(mAnonymousAccount, getContext()); + + assertNotSame("Got same client instances for different accounts", + client1, client2); + assertSame("Got different client instances for same account", + client1, mSSMgr.getClientFor(mValidAccount, getContext())); + + // TODO harder tests + } + + public void testRemoveClientFor() { + OwnCloudClient client1 = mSSMgr.getClientFor(mValidAccount, getContext()); + mSSMgr.removeClientFor(mValidAccount); + assertNotSame("Got same client instance after removing it from manager", + client1, mSSMgr.getClientFor(mValidAccount, getContext())); + + // TODO harder tests + } + + + public void testSaveAllClients() { + // TODO implement test; + // or refactor saveAllClients() method out of OwnCloudClientManager to make + // it independent of AccountManager + } + +}