From: David A. Velasco Date: Thu, 24 Jan 2013 09:29:11 +0000 (+0100) Subject: Cleaning-up AuthenticatorActivty code X-Git-Tag: oc-android-1.4.3~29^2~19 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/088b1e83a46c97bc2d9f37654892a2d8a29bc029 Cleaning-up AuthenticatorActivty code --- diff --git a/res/layout-land/account_setup.xml b/res/layout-land/account_setup.xml index d68feea3..b30c5150 100644 --- a/res/layout-land/account_setup.xml +++ b/res/layout-land/account_setup.xml @@ -58,7 +58,7 @@ android:layout_weight="1" > @@ -104,7 +105,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:checked="false" - android:onClick="onOff_check_Click" + android:onClick="onCheckClick" android:text="@string/oauth_check_onoff" android:textAppearance="?android:attr/textAppearanceSmall" /> @@ -116,19 +117,34 @@ android:text="@string/auth_login_details" android:textAppearance="?android:attr/textAppearanceSmall" /> - - - - - + + + + + + + + + + diff --git a/res/layout/account_setup.xml b/res/layout/account_setup.xml index 6790529a..7d77e515 100644 --- a/res/layout/account_setup.xml +++ b/res/layout/account_setup.xml @@ -53,7 +53,7 @@ android:layout_weight="1" > @@ -100,7 +101,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:checked="false" - android:onClick="onOff_check_Click" + android:onClick="onCheckClick" android:text="@string/oauth_check_onoff" android:textAppearance="?android:attr/textAppearanceSmall" /> @@ -113,12 +114,27 @@ android:textAppearance="?android:attr/textAppearanceSmall" /> + + + + + @@ -170,11 +186,12 @@ android:inputType="textPassword"/> diff --git a/res/values/oauth.xml b/res/values/oauth.xml new file mode 100644 index 00000000..b12b648c --- /dev/null +++ b/res/values/oauth.xml @@ -0,0 +1,5 @@ + + + https://frko.surfnetlabs.nl/workshop/php-oauth/authorize.php + https://frko.surfnetlabs.nl/workshop/php-oauth/token.php + diff --git a/res/values/strings.xml b/res/values/strings.xml index 5afdd86e..3609f563 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -179,6 +179,7 @@ Invalid login / password Wrong path given Internal server error, code %1$d + Unexpected state; please, enter the server URL again Application terminated unexpectedly. Would you like to submit a crash report? Send report diff --git a/src/com/owncloud/android/AccountUtils.java b/src/com/owncloud/android/AccountUtils.java index 997633aa..06ab31a9 100644 --- a/src/com/owncloud/android/AccountUtils.java +++ b/src/com/owncloud/android/AccountUtils.java @@ -97,10 +97,10 @@ public class AccountUtils { * @return webdav path for given OC version, null if OC version unknown */ public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth) { - if (supportsOAuth) { - return ODAV_PATH; - } if (version != null) { + if (supportsOAuth) { + return ODAV_PATH; + } if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0) return WEBDAV_PATH_4_0; if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0 diff --git a/src/com/owncloud/android/Uploader.java b/src/com/owncloud/android/Uploader.java index 6ec8c4fd..02579d75 100644 --- a/src/com/owncloud/android/Uploader.java +++ b/src/com/owncloud/android/Uploader.java @@ -138,8 +138,8 @@ public class Uploader extends ListActivity implements OnItemClickListener, andro // in API7 < this constatant is defined in // Settings.ADD_ACCOUNT_SETTINGS // and Settings.EXTRA_AUTHORITIES - Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); - intent.putExtra("authorities", new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE }); + Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT); + intent.putExtra("authorities", new String[] { AccountAuthenticator.AUTHORITY }); startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT); } else { // since in API7 there is no direct call for diff --git a/src/com/owncloud/android/authenticator/AccountAuthenticator.java b/src/com/owncloud/android/authenticator/AccountAuthenticator.java index 391e9e89..c6ad9e7b 100644 --- a/src/com/owncloud/android/authenticator/AccountAuthenticator.java +++ b/src/com/owncloud/android/authenticator/AccountAuthenticator.java @@ -32,6 +32,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { * used by application and all extensions. */ public static final String ACCOUNT_TYPE = "owncloud"; + public static final String AUTHORITY = "org.owncloud"; public static final String AUTH_TOKEN_TYPE = "org.owncloud"; public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password"; public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token"; @@ -93,13 +94,13 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { return e.getFailureBundle(); } final Intent intent = new Intent(mContext, AuthenticatorActivity.class); - intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, - response); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType); intent.putExtra(KEY_REQUIRED_FEATURES, requiredFeatures); intent.putExtra(KEY_LOGIN_OPTIONS, options); setIntentFlags(intent); + final Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); return bundle; @@ -138,10 +139,14 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { return null; } + /** + * {@inheritDoc} + */ @Override public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { + /// validate parameters try { validateAccountType(account.type); validateAuthTokenType(authTokenType); @@ -151,34 +156,29 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { e.printStackTrace(); return e.getFailureBundle(); } + + /// check if required token is stored final AccountManager am = AccountManager.get(mContext); - if (authTokenType.equals(AUTH_TOKEN_TYPE_ACCESS_TOKEN)) { - final String accessToken = am.peekAuthToken(account, authTokenType); - if (accessToken != null) { - final Bundle result = new Bundle(); - result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); - result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE); - result.putString(AccountManager.KEY_AUTHTOKEN, accessToken); - return result; - } - - } else if (authTokenType.equals(AUTH_TOKEN_TYPE_PASSWORD)) { - final String password = am.getPassword(account); - if (password != null) { - final Bundle result = new Bundle(); - result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); - result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE); - result.putString(AccountManager.KEY_AUTHTOKEN, password); - return result; - } + String accessToken; + if (authTokenType.equals(AUTH_TOKEN_TYPE_PASSWORD)) { + accessToken = am.getPassword(account); + } else { + accessToken = am.peekAuthToken(account, authTokenType); } - + if (accessToken != null) { + final Bundle result = new Bundle(); + result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); + result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE); + result.putString(AccountManager.KEY_AUTHTOKEN, accessToken); + return result; + } + + /// if not stored, return Intent to access the AuthenticatorActivity final Intent intent = new Intent(mContext, AuthenticatorActivity.class); - intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, - response); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType); intent.putExtra(KEY_LOGIN_OPTIONS, options); - intent.putExtra(AuthenticatorActivity.PARAM_USERNAME, account.name); // TODO fix, this will pass the accountName, not the username + intent.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account); final Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); @@ -224,8 +224,8 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { private void setIntentFlags(Intent intent) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - //intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - //intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); // incompatible with the authorization code grant in OAuth + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); // incompatible with the authorization code grant in OAuth intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); intent.addFlags(Intent.FLAG_FROM_BACKGROUND); } diff --git a/src/com/owncloud/android/authenticator/AuthenticationRunnable.java b/src/com/owncloud/android/authenticator/AuthenticationRunnable.java deleted file mode 100644 index 63f89bc6..00000000 --- a/src/com/owncloud/android/authenticator/AuthenticationRunnable.java +++ /dev/null @@ -1,88 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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.authenticator; - -import java.net.URL; - -import org.apache.commons.httpclient.HttpStatus; - -import com.owncloud.android.R; -import com.owncloud.android.network.OwnCloudClientUtils; - -import eu.alefzero.webdav.WebdavClient; - -import android.content.Context; -import android.net.Uri; -import android.os.Handler; - -public class AuthenticationRunnable implements Runnable { - - private OnAuthenticationResultListener mListener; - private Handler mHandler; - private URL mUrl; - private String mUsername; - private String mPassword; - private Context mContext; - - public AuthenticationRunnable(URL url, String username, String password, Context context) { - mListener = null; - mUrl = url; - mUsername = username; - mPassword = password; - mContext = context; - } - - public void setOnAuthenticationResultListener( - OnAuthenticationResultListener listener, Handler handler) { - mListener = listener; - mHandler = handler; - } - - @Override - public void run() { - Uri uri; - uri = Uri.parse(mUrl.toString()); - WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(uri, mUsername, mPassword, mContext); - int login_result = wdc.tryToLogin(); - switch (login_result) { - case HttpStatus.SC_OK: - postResult(true, uri.toString()); - break; - case HttpStatus.SC_UNAUTHORIZED: - postResult(false, mContext.getString(R.string.auth_unauthorized)); - break; - case HttpStatus.SC_NOT_FOUND: - postResult(false, mContext.getString(R.string.auth_not_found)); - break; - default: - postResult(false, String.format(mContext.getString(R.string.auth_internal), login_result)); - } - } - - private void postResult(final boolean success, final String message) { - if (mHandler != null && mListener != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - mListener.onAuthenticationResult(success, message); - } - }); - } - } -} diff --git a/src/com/owncloud/android/authenticator/OnAuthenticationResultListener.java b/src/com/owncloud/android/authenticator/OnAuthenticationResultListener.java deleted file mode 100644 index f7d127ff..00000000 --- a/src/com/owncloud/android/authenticator/OnAuthenticationResultListener.java +++ /dev/null @@ -1,25 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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.authenticator; - -public interface OnAuthenticationResultListener { - - public void onAuthenticationResult(boolean success, String message); - -} diff --git a/src/com/owncloud/android/authenticator/OnConnectCheckListener.java b/src/com/owncloud/android/authenticator/OnConnectCheckListener.java deleted file mode 100644 index f0ad3e99..00000000 --- a/src/com/owncloud/android/authenticator/OnConnectCheckListener.java +++ /dev/null @@ -1,29 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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.authenticator; - -public interface OnConnectCheckListener { - - enum ResultType { - OK_SSL, OK_NO_SSL, SSL_INIT_ERROR, HOST_NOT_AVAILABLE, TIMEOUT, NO_NETWORK_CONNECTION, INCORRECT_ADDRESS, INSTANCE_NOT_CONFIGURED, FILE_NOT_FOUND, UNKNOWN_ERROR, WRONG_CONNECTION, SSL_UNVERIFIED_SERVER, BAD_OC_VERSION - } - - public void onConnectionCheckResult(ResultType type); - -} diff --git a/src/com/owncloud/android/authenticator/oauth2/OAuth2Context.java b/src/com/owncloud/android/authenticator/oauth2/OAuth2Context.java index fbc509d9..fa0872e4 100644 --- a/src/com/owncloud/android/authenticator/oauth2/OAuth2Context.java +++ b/src/com/owncloud/android/authenticator/oauth2/OAuth2Context.java @@ -18,8 +18,8 @@ public class OAuth2Context { public static final String OAUTH2_G_DEVICE_GETTOKEN_URL = "https://accounts.google.com/o/oauth2/token"; public static final String OAUTH2_G_DEVICE_GETCODE_SCOPES = "https://www.googleapis.com/auth/userinfo.email"; - public static final String OAUTH2_F_AUTHORIZATION_ENDPOINT_URL = "https://frko.surfnetlabs.nl/workshop/php-oauth/authorize.php"; - public static final String OAUTH2_F_TOKEN_ENDPOINT_URL = "https://frko.surfnetlabs.nl/workshop/php-oauth/token.php"; + //public static final String OAUTH2_F_AUTHORIZATION_ENDPOINT_URL = "https://frko.surfnetlabs.nl/workshop/php-oauth/authorize.php"; + //public static final String OAUTH2_F_TOKEN_ENDPOINT_URL = "https://frko.surfnetlabs.nl/workshop/php-oauth/token.php"; public static final String OAUTH2_F_CLIENT_ID = "oc-android-test"; public static final String OAUTH2_F_SCOPE = "grades"; @@ -42,4 +42,15 @@ public class OAuth2Context { public static final String KEY_GRANT_TYPE = "grant_type"; public static final String KEY_CODE = "code"; public static final String KEY_CLIENT_ID = "client_id"; + + public static final String CODE_USER_CODE = "user_code"; + public static final String CODE_CLIENT_ID = "client_id"; + public static final String CODE_SCOPE = "scope"; + public static final String CODE_VERIFICATION_URL = "verification_url"; + public static final String CODE_EXPIRES_IN = "expires_in"; + public static final String CODE_DEVICE_CODE = "device_code"; + public static final String CODE_INTERVAL = "interval"; + public static final String CODE_RESPONSE_TYPE = "response_type"; + public static final String CODE_REDIRECT_URI = "redirect_uri"; + } diff --git a/src/com/owncloud/android/authenticator/oauth2/OAuth2GetCodeRunnable.java b/src/com/owncloud/android/authenticator/oauth2/OAuth2GetCodeRunnable.java deleted file mode 100644 index 62085b2d..00000000 --- a/src/com/owncloud/android/authenticator/oauth2/OAuth2GetCodeRunnable.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.owncloud.android.authenticator.oauth2; - -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.http.NameValuePair; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.message.BasicNameValuePair; -import org.json.JSONException; -import org.json.JSONObject; - -import android.content.Context; -import android.content.Intent; -import android.net.ConnectivityManager; -import android.net.Uri; -import android.os.Handler; -import android.util.Log; - -import com.owncloud.android.authenticator.oauth2.OnOAuth2GetCodeResultListener.ResultOAuthType; -import com.owncloud.android.authenticator.oauth2.connection.ConnectorOAuth2; - -/** - * Implements the communication with oAuth2 server to get User Code and other useful values. - * - * @author SolidGear S.L. - * - */ -public class OAuth2GetCodeRunnable implements Runnable { - - public static final String CODE_USER_CODE = "user_code"; - public static final String CODE_CLIENT_ID = "client_id"; - public static final String CODE_SCOPE = "scope"; - public static final String CODE_VERIFICATION_URL = "verification_url"; - public static final String CODE_EXPIRES_IN = "expires_in"; - public static final String CODE_DEVICE_CODE = "device_code"; - public static final String CODE_INTERVAL = "interval"; - - private static final String CODE_RESPONSE_TYPE = "response_type"; - private static final String CODE_REDIRECT_URI = "redirect_uri"; - - private String mGrantType = OAuth2Context.OAUTH2_AUTH_CODE_GRANT_TYPE; - - private static final String TAG = "OAuth2GetCodeRunnable"; - private OnOAuth2GetCodeResultListener mListener; - private String mUrl; - private Handler mHandler; - private Context mContext; - private JSONObject codeResponseJson = null; - ResultOAuthType mLatestResult; - - - public void setListener(OnOAuth2GetCodeResultListener listener, Handler handler) { - mListener = listener; - mHandler = handler; - } - - public OAuth2GetCodeRunnable(String url, Context context) { - mListener = null; - mHandler = null; - mUrl = url; - mContext = context; - } - - @Override - public void run() { - - if (!isOnline()) { - postResult(ResultOAuthType.NO_NETWORK_CONNECTION,null); - return; - } - - if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) { - mLatestResult = (mUrl.startsWith("https://"))? ResultOAuthType.OK_SSL : ResultOAuthType.OK_NO_SSL; - } else { - mUrl = "https://" + mUrl; - mLatestResult = ResultOAuthType.OK_SSL; - } - - if (mGrantType.equals(OAuth2Context.OAUTH2_AUTH_CODE_GRANT_TYPE)) { - requestBrowserToGetAuthorizationCode(); - - } else if (mGrantType.equals(OAuth2Context.OAUTH_G_DEVICE_GETTOKEN_GRANT_TYPE)) { - getAuthorizationCode(); - } - } - - /// open the authorization endpoint in a web browser! - private void requestBrowserToGetAuthorizationCode() { - Uri uri = Uri.parse(mUrl); - Uri.Builder uriBuilder = uri.buildUpon(); - uriBuilder.appendQueryParameter(CODE_RESPONSE_TYPE, OAuth2Context.OAUTH2_CODE_RESPONSE_TYPE); - uriBuilder.appendQueryParameter(CODE_REDIRECT_URI, OAuth2Context.MY_REDIRECT_URI); - uriBuilder.appendQueryParameter(CODE_CLIENT_ID, OAuth2Context.OAUTH2_F_CLIENT_ID); - uriBuilder.appendQueryParameter(CODE_SCOPE, OAuth2Context.OAUTH2_F_SCOPE); - //uriBuilder.appendQueryParameter(CODE_STATE, whateverwewant); - - uri = uriBuilder.build(); - Log.d(TAG, "Starting browser to view " + uri.toString()); - - Intent i = new Intent(Intent.ACTION_VIEW, uri); - mContext.startActivity(i); - - postResult(mLatestResult, null); - } - - - private void getAuthorizationCode() { - ConnectorOAuth2 connectorOAuth2 = new ConnectorOAuth2(mUrl); - try { - List nameValuePairs = new ArrayList(2); - nameValuePairs.add(new BasicNameValuePair(CODE_CLIENT_ID, OAuth2Context.OAUTH2_G_DEVICE_CLIENT_ID)); - nameValuePairs.add(new BasicNameValuePair(CODE_SCOPE,OAuth2Context.OAUTH2_G_DEVICE_GETCODE_SCOPES)); - UrlEncodedFormEntity params = new UrlEncodedFormEntity(nameValuePairs); - codeResponseJson = new JSONObject(connectorOAuth2.connPost(params)); - } catch (JSONException e) { - Log.e(TAG, "JSONException converting to Json: " + e.toString()); - } catch (UnsupportedEncodingException e) { - Log.e(TAG, "UnsupportedEncodingException encoding URL values: " + e.toString()); - } catch (Exception e) { - Log.e(TAG, "Exception : " + e.toString()); - } - - if (codeResponseJson == null) { - mLatestResult = ResultOAuthType.HOST_NOT_AVAILABLE; - } - postResult(mLatestResult, codeResponseJson); - } - - - private boolean isOnline() { - ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - return cm != null && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnectedOrConnecting(); - } - - private void postResult(final ResultOAuthType result,final JSONObject codeResponseJson) { - if (mHandler != null && mListener != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - mListener.onOAuth2GetCodeResult(result, codeResponseJson); - } - }); - } - } - -} \ No newline at end of file diff --git a/src/com/owncloud/android/network/OwnCloudClientUtils.java b/src/com/owncloud/android/network/OwnCloudClientUtils.java index b334fdff..c4462188 100644 --- a/src/com/owncloud/android/network/OwnCloudClientUtils.java +++ b/src/com/owncloud/android/network/OwnCloudClientUtils.java @@ -130,26 +130,7 @@ public class OwnCloudClientUtils { return client; } - - /** - * Creates a WebdavClient to try a new account before saving it - * - * @param uri URL to the ownCloud server - * @param username User name - * @param password User password - * @param context Android context where the WebdavClient is being created. - * @return A WebdavClient object ready to be used - */ - public static WebdavClient createOwnCloudClient(Uri uri, String username, String password, Context context) { - //Log.d(TAG, "Creating WebdavClient for " + username + "@" + uri); - - WebdavClient client = createOwnCloudClient(uri, context); - - client.setBasicCredentials(username, password); - - return client; - } - + /** * Creates a WebdavClient to access a URL and sets the desired parameters for ownCloud client connections. diff --git a/src/com/owncloud/android/operations/ConnectionCheckOperation.java b/src/com/owncloud/android/operations/ConnectionCheckOperation.java deleted file mode 100644 index 558e0df2..00000000 --- a/src/com/owncloud/android/operations/ConnectionCheckOperation.java +++ /dev/null @@ -1,138 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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.operations; - -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.httpclient.methods.GetMethod; -import org.json.JSONException; -import org.json.JSONObject; - -import com.owncloud.android.AccountUtils; -import com.owncloud.android.utils.OwnCloudVersion; - -import eu.alefzero.webdav.WebdavClient; -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.Uri; -import android.util.Log; - -public class ConnectionCheckOperation extends RemoteOperation { - - /** Maximum time to wait for a response from the server when the connection is being tested, in MILLISECONDs. */ - public static final int TRY_CONNECTION_TIMEOUT = 5000; - - private static final String TAG = ConnectionCheckOperation.class.getSimpleName(); - - private String mUrl; - private RemoteOperationResult mLatestResult; - private Context mContext; - private OwnCloudVersion mOCVersion; - - public ConnectionCheckOperation(String url, Context context) { - mUrl = url; - mContext = context; - mOCVersion = null; - } - - public OwnCloudVersion getDiscoveredVersion() { - return mOCVersion; - } - - private boolean tryConnection(WebdavClient wc, String urlSt) { - boolean retval = false; - GetMethod get = null; - try { - get = new GetMethod(urlSt); - int status = wc.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("installed")) { - mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED); - } else { - mOCVersion = new OwnCloudVersion(json.getString("version")); - if (!mOCVersion.isVersionValid()) { - mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION); - - } else { - mLatestResult = new RemoteOperationResult(urlSt.startsWith("https://") ? - RemoteOperationResult.ResultCode.OK_SSL : - RemoteOperationResult.ResultCode.OK_NO_SSL - ); - - retval = true; - } - } - - } else { - mLatestResult = new RemoteOperationResult(false, status); - } - - } catch (JSONException e) { - mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED); - - } catch (Exception e) { - mLatestResult = new RemoteOperationResult(e); - - } finally { - if (get != null) - get.releaseConnection(); - } - - if (mLatestResult.isSuccess()) { - Log.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage()); - - } else if (mLatestResult.getException() != null) { - Log.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException()); - - } else { - Log.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage()); - } - - return retval; - } - - private boolean isOnline() { - ConnectivityManager cm = (ConnectivityManager) mContext - .getSystemService(Context.CONNECTIVITY_SERVICE); - return cm != null && cm.getActiveNetworkInfo() != null - && cm.getActiveNetworkInfo().isConnectedOrConnecting(); - } - - @Override - protected RemoteOperationResult run(WebdavClient client) { - if (!isOnline()) { - return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION); - } - if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) { - tryConnection(client, mUrl + AccountUtils.STATUS_PATH); - - } else { - client.setBaseUri(Uri.parse("https://" + mUrl + AccountUtils.STATUS_PATH)); - boolean httpsSuccess = tryConnection(client, "https://" + mUrl + AccountUtils.STATUS_PATH); - if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) { - Log.d(TAG, "establishing secure connection failed, trying non secure connection"); - client.setBaseUri(Uri.parse("http://" + mUrl + AccountUtils.STATUS_PATH)); - tryConnection(client, "http://" + mUrl + AccountUtils.STATUS_PATH); - } - } - return mLatestResult; - } - -} diff --git a/src/com/owncloud/android/operations/ExistenceCheckOperation.java b/src/com/owncloud/android/operations/ExistenceCheckOperation.java index e5f29bfd..d678ac34 100644 --- a/src/com/owncloud/android/operations/ExistenceCheckOperation.java +++ b/src/com/owncloud/android/operations/ExistenceCheckOperation.java @@ -41,20 +41,6 @@ public class ExistenceCheckOperation extends RemoteOperation { private String mPath; private Context mContext; private boolean mSuccessIfAbsent; - private String mAccessToken; - - - /** - * Simple constructor. Success when the path in the server exists. - * - * @param path Path to append to the URL owned by the client instance. - * @param context Android application context. - * @param accessToken Access token for Bearer Authentication -> TODO: move to other place - */ - public ExistenceCheckOperation(String path, Context context, String accessToken) { - this(path, context, false); - mAccessToken = accessToken; - } /** @@ -80,8 +66,6 @@ public class ExistenceCheckOperation extends RemoteOperation { HeadMethod head = null; try { head = new HeadMethod(client.getBaseUri() + mPath); - head.addRequestHeader("Authorization", "Bearer " + mAccessToken); // TODO put in some general place - int status = client.executeMethod(head, TIMEOUT, TIMEOUT); client.exhaustResponse(head.getResponseBodyAsStream()); boolean success = (status == HttpStatus.SC_OK && !mSuccessIfAbsent) || (status == HttpStatus.SC_NOT_FOUND && mSuccessIfAbsent); @@ -107,8 +91,4 @@ public class ExistenceCheckOperation extends RemoteOperation { } - public String getAccessToken() { - return mAccessToken; - } - } diff --git a/src/com/owncloud/android/operations/GetOAuth2AccessToken.java b/src/com/owncloud/android/operations/GetOAuth2AccessToken.java deleted file mode 100644 index 82de8441..00000000 --- a/src/com/owncloud/android/operations/GetOAuth2AccessToken.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.owncloud.android.operations; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.commons.httpclient.NameValuePair; -import org.json.JSONException; -import org.json.JSONObject; - -import com.owncloud.android.authenticator.oauth2.OAuth2Context; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; - -import android.util.Log; - -import eu.alefzero.webdav.WebdavClient; - -public class GetOAuth2AccessToken extends RemoteOperation { - - private static final String TAG = GetOAuth2AccessToken.class.getSimpleName(); - - private Map mOAuth2AuthorizationResponse; - private Map mResultTokenMap; - - - public GetOAuth2AccessToken(Map oAuth2AuthorizationResponse) { - mOAuth2AuthorizationResponse = oAuth2AuthorizationResponse; - mResultTokenMap = null; - } - - - public Map getOauth2AutorizationResponse() { - return mOAuth2AuthorizationResponse; - } - - public Map getResultTokenMap() { - return mResultTokenMap; - } - - @Override - protected RemoteOperationResult run(WebdavClient client) { - RemoteOperationResult result = null; - PostMethod postMethod = null; - - try { - NameValuePair[] nameValuePairs = new NameValuePair[5]; - nameValuePairs[0] = new NameValuePair(OAuth2Context.KEY_CLIENT_ID, OAuth2Context.OAUTH2_F_CLIENT_ID); - nameValuePairs[1] = new NameValuePair(OAuth2Context.KEY_CODE, mOAuth2AuthorizationResponse.get(OAuth2Context.KEY_CODE)); - nameValuePairs[2] = new NameValuePair(OAuth2Context.KEY_SCOPE, mOAuth2AuthorizationResponse.get(OAuth2Context.KEY_SCOPE)); - nameValuePairs[3] = new NameValuePair(OAuth2Context.KEY_REDIRECT_URI, OAuth2Context.MY_REDIRECT_URI); - nameValuePairs[4] = new NameValuePair(OAuth2Context.KEY_GRANT_TYPE, OAuth2Context.OAUTH2_AUTH_CODE_GRANT_TYPE); - - postMethod = new PostMethod(client.getBaseUri().toString()); - postMethod.setRequestBody(nameValuePairs); - int status = client.executeMethod(postMethod); - if (status >= 300) { - client.exhaustResponse(postMethod.getResponseBodyAsStream()); - result = new RemoteOperationResult(false, status); - - } else { - JSONObject tokenJson = new JSONObject(postMethod.getResponseBodyAsString()); - parseResult(tokenJson); - if (mResultTokenMap.get(OAuth2Context.OAUTH2_TOKEN_RECEIVED_ERROR) != null) { - result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR); - - } else { - result = new RemoteOperationResult(true, status); - } - } - - } catch (Exception e) { - result = new RemoteOperationResult(e); - - } finally { - if (postMethod != null) - postMethod.releaseConnection(); // let the connection available for other methods - - if (result.isSuccess()) { - Log.i(TAG, "OAuth2 TOKEN REQUEST with code " + mOAuth2AuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage()); - - } else if (result.getException() != null) { - Log.e(TAG, "OAuth2 TOKEN REQUEST with code " + mOAuth2AuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage(), result.getException()); - - } else if (result.getCode() == ResultCode.OAUTH2_ERROR) { - Log.e(TAG, "OAuth2 TOKEN REQUEST with code " + mOAuth2AuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + mResultTokenMap.get(OAuth2Context.OAUTH2_TOKEN_RECEIVED_ERROR)); - - } else { - Log.e(TAG, "OAuth2 TOKEN REQUEST with code " + mOAuth2AuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage()); - } - } - - return result; - } - - - private void parseResult (JSONObject tokenJson) throws JSONException { - mResultTokenMap = new HashMap(); - - if (tokenJson.has(OAuth2Context.KEY_ACCESS_TOKEN)) { - mResultTokenMap.put(OAuth2Context.KEY_ACCESS_TOKEN, tokenJson.getString(OAuth2Context.KEY_ACCESS_TOKEN)); - } - if (tokenJson.has(OAuth2Context.KEY_TOKEN_TYPE)) { - mResultTokenMap.put(OAuth2Context.KEY_TOKEN_TYPE, tokenJson.getString(OAuth2Context.KEY_TOKEN_TYPE)); - } - if (tokenJson.has(OAuth2Context.KEY_EXPIRES_IN)) { - mResultTokenMap.put(OAuth2Context.KEY_EXPIRES_IN, tokenJson.getString(OAuth2Context.KEY_EXPIRES_IN)); - } - if (tokenJson.has(OAuth2Context.KEY_REFRESH_TOKEN)) { - mResultTokenMap.put(OAuth2Context.KEY_REFRESH_TOKEN, tokenJson.getString(OAuth2Context.KEY_REFRESH_TOKEN)); - } - if (tokenJson.has(OAuth2Context.KEY_SCOPE)) { - mResultTokenMap.put(OAuth2Context.KEY_SCOPE, tokenJson.getString(OAuth2Context.KEY_SCOPE)); - } - if (tokenJson.has(OAuth2Context.KEY_ERROR)) { - mResultTokenMap.put(OAuth2Context.KEY_ERROR, tokenJson.getString(OAuth2Context.KEY_ERROR)); - } - if (tokenJson.has(OAuth2Context.KEY_ERROR_DESCRIPTION)) { - mResultTokenMap.put(OAuth2Context.KEY_ERROR_DESCRIPTION, tokenJson.getString(OAuth2Context.KEY_ERROR_DESCRIPTION)); - } - if (tokenJson.has(OAuth2Context.KEY_ERROR_URI)) { - mResultTokenMap.put(OAuth2Context.KEY_ERROR_URI, tokenJson.getString(OAuth2Context.KEY_ERROR_URI)); - } - } - -} diff --git a/src/com/owncloud/android/ui/activity/AccountSelectActivity.java b/src/com/owncloud/android/ui/activity/AccountSelectActivity.java index 21736e1d..f6b2a79d 100644 --- a/src/com/owncloud/android/ui/activity/AccountSelectActivity.java +++ b/src/com/owncloud/android/ui/activity/AccountSelectActivity.java @@ -94,10 +94,10 @@ public class AccountSelectActivity extends SherlockListActivity implements /// the account set as default changed since this activity was created // trigger synchronization - ContentResolver.cancelSync(null, AccountAuthenticator.AUTH_TOKEN_TYPE); + ContentResolver.cancelSync(null, AccountAuthenticator.AUTHORITY); Bundle bundle = new Bundle(); bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); - ContentResolver.requestSync(AccountUtils.getCurrentOwnCloudAccount(this), AccountAuthenticator.AUTH_TOKEN_TYPE, bundle); + ContentResolver.requestSync(AccountUtils.getCurrentOwnCloudAccount(this), AccountAuthenticator.AUTHORITY, bundle); // restart the main activity Intent i = new Intent(this, FileDisplayActivity.class); @@ -135,7 +135,7 @@ public class AccountSelectActivity extends SherlockListActivity implements Intent intent = new Intent( android.provider.Settings.ACTION_ADD_ACCOUNT); intent.putExtra("authorities", - new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE }); + new String[] { AccountAuthenticator.AUTHORITY }); startActivity(intent); return true; } diff --git a/src/com/owncloud/android/ui/activity/AuthenticatorActivity.java b/src/com/owncloud/android/ui/activity/AuthenticatorActivity.java index fc84df60..4d17a831 100644 --- a/src/com/owncloud/android/ui/activity/AuthenticatorActivity.java +++ b/src/com/owncloud/android/ui/activity/AuthenticatorActivity.java @@ -1,5 +1,6 @@ /* ownCloud Android client application * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,31 +19,19 @@ package com.owncloud.android.ui.activity; -import java.net.MalformedURLException; -import java.net.URL; import java.util.HashMap; import java.util.Map; -import org.json.JSONException; -import org.json.JSONObject; - import com.owncloud.android.AccountUtils; import com.owncloud.android.authenticator.AccountAuthenticator; -import com.owncloud.android.authenticator.AuthenticationRunnable; -import com.owncloud.android.authenticator.OnAuthenticationResultListener; -import com.owncloud.android.authenticator.OnConnectCheckListener; import com.owncloud.android.authenticator.oauth2.OAuth2Context; -import com.owncloud.android.authenticator.oauth2.OAuth2GetCodeRunnable; -import com.owncloud.android.authenticator.oauth2.OnOAuth2GetCodeResultListener; -import com.owncloud.android.authenticator.oauth2.connection.ConnectorOAuth2; -import com.owncloud.android.authenticator.oauth2.services.OAuth2GetTokenService; import com.owncloud.android.ui.dialog.SslValidatorDialog; import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener; import com.owncloud.android.utils.OwnCloudVersion; import com.owncloud.android.network.OwnCloudClientUtils; -import com.owncloud.android.operations.ConnectionCheckOperation; +import com.owncloud.android.operations.OwnCloudServerCheckOperation; import com.owncloud.android.operations.ExistenceCheckOperation; -import com.owncloud.android.operations.GetOAuth2AccessToken; +import com.owncloud.android.operations.OAuth2GetAccessToken; import com.owncloud.android.operations.OnRemoteOperationListener; import com.owncloud.android.operations.RemoteOperation; import com.owncloud.android.operations.RemoteOperationResult; @@ -53,12 +42,9 @@ import android.accounts.AccountManager; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; -import android.content.BroadcastReceiver; import android.content.ContentResolver; -import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.IntentFilter; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; @@ -67,7 +53,6 @@ import android.preference.PreferenceManager; import android.text.InputType; import android.util.Log; import android.view.View; -import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.view.Window; import android.widget.CheckBox; @@ -83,204 +68,815 @@ import eu.alefzero.webdav.WebdavClient; * This Activity is used to add an ownCloud account to the App * * @author Bartek Przybylski - * + * @author David A. Velasco */ public class AuthenticatorActivity extends AccountAuthenticatorActivity - implements OnAuthenticationResultListener, OnConnectCheckListener, OnRemoteOperationListener, OnSslValidatorListener, - OnFocusChangeListener, OnClickListener, OnOAuth2GetCodeResultListener { + implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeListener { + + private static final String TAG = AuthenticatorActivity.class.getSimpleName(); + + public static final String EXTRA_ACCOUNT = "ACCOUNT"; + public static final String EXTRA_USER_NAME = "USER_NAME"; + public static final String EXTRA_HOST_NAME = "HOST_NAME"; + + private static final String KEY_HOST_URL_TEXT = "HOST_URL_TEXT"; + private static final String KEY_OC_VERSION = "OC_VERSION"; + private static final String KEY_STATUS_TEXT = "STATUS_TEXT"; + private static final String KEY_STATUS_ICON = "STATUS_ICON"; + private static final String KEY_STATUS_CORRECT = "STATUS_CORRECT"; + private static final String KEY_IS_SSL_CONN = "IS_SSL_CONN"; + private static final String KEY_OAUTH2_STATUS_TEXT = "OAUTH2_STATUS_TEXT"; + private static final String KEY_OAUTH2_STATUS_ICON = "OAUTH2_STATUS_ICON"; private static final int DIALOG_LOGIN_PROGRESS = 0; private static final int DIALOG_SSL_VALIDATOR = 1; private static final int DIALOG_CERT_NOT_SAVED = 2; + private static final int DIALOG_OAUTH2_LOGIN_PROGRESS = 3; - private static final String TAG = "AuthActivity"; - - private Thread mAuthThread; - private AuthenticationRunnable mAuthRunnable; - private ConnectionCheckOperation mConnChkRunnable; - private ExistenceCheckOperation mAuthChkOperation; - private final Handler mHandler = new Handler(); - private String mBaseUrl; + + private String mHostBaseUrl; private OwnCloudVersion mDiscoveredVersion; - private static final String STATUS_TEXT = "STATUS_TEXT"; - private static final String STATUS_ICON = "STATUS_ICON"; - private static final String STATUS_CORRECT = "STATUS_CORRECT"; - private static final String IS_SSL_CONN = "IS_SSL_CONN"; - private static final String OC_VERSION = "OC_VERSION"; private int mStatusText, mStatusIcon; private boolean mStatusCorrect, mIsSslConn; + private int mOAuth2StatusText, mOAuth2StatusIcon; + + private final Handler mHandler = new Handler(); + private Thread mOperationThread; + private OwnCloudServerCheckOperation mOcServerChkOperation; + private ExistenceCheckOperation mAuthCheckOperation; private RemoteOperationResult mLastSslUntrustedServerResult; - public static final String PARAM_ACCOUNTNAME = "param_Accountname"; - - public static final String PARAM_USERNAME = "param_Username"; - public static final String PARAM_HOSTNAME = "param_Hostname"; - - // oAuth2 variables. - private static final int OAUTH2_LOGIN_PROGRESS = 3; - private static final String OAUTH2_STATUS_TEXT = "OAUTH2_STATUS_TEXT"; - private static final String OAUTH2_STATUS_ICON = "OAUTH2_STATUS_ICON"; - private static final String OAUTH2_CODE_RESULT = "CODE_RESULT"; - private static final String OAUTH2_IS_CHECKED = "OAUTH2_IS_CHECKED"; - private Thread mOAuth2GetCodeThread; - private OAuth2GetCodeRunnable mOAuth2GetCodeRunnable; - private TokenReceiver tokenReceiver; - private JSONObject codeResponseJson; - private int mOAuth2StatusText, mOAuth2StatusIcon; + //private Thread mOAuth2GetCodeThread; + //private OAuth2GetAuthorizationToken mOAuth2GetCodeRunnable; + //private TokenReceiver tokenReceiver; + //private JSONObject mCodeResponseJson; + private Uri mNewCapturedUriFromOAuth2Redirection; - public ConnectorOAuth2 connectorOAuth2; + private AccountManager mAccountMgr; - // Variables used to save the on the state the contents of all fields. - private static final String HOST_URL_TEXT = "HOST_URL_TEXT"; - private static final String ACCOUNT_USERNAME = "ACCOUNT_USERNAME"; - private static final String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD"; + private ImageView mRefreshButton; + private ImageView mViewPasswordButton; + private EditText mHostUrlInput; + private EditText mUsernameInput; + private EditText mPasswordInput; + private CheckBox mOAuth2Check; + private String mOAuthAccessToken; + private View mOkButton; - //private boolean mNewRedirectUriCaptured; - private Uri mNewCapturedUriFromOAuth2Redirection; + private TextView mOAuthAuthEndpointText; + private TextView mOAuthTokenEndpointText; - // END of oAuth2 variables. + + + /** + * {@inheritDoc} + * + * IMPORTANT ENTRY POINT 1: activity is shown to the user + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_NO_TITLE); + + /// set view and get references to view elements setContentView(R.layout.account_setup); - ImageView iv = (ImageView) findViewById(R.id.refreshButton); - ImageView iv2 = (ImageView) findViewById(R.id.viewPassword); - TextView tv = (TextView) findViewById(R.id.host_URL); - TextView tv2 = (TextView) findViewById(R.id.account_password); - EditText oauth2Url = (EditText)findViewById(R.id.oAuth_URL); - oauth2Url.setText("OWNCLOUD AUTHORIZATION PROVIDER IN TEST"); - - if (savedInstanceState != null) { - mStatusIcon = savedInstanceState.getInt(STATUS_ICON); - mStatusText = savedInstanceState.getInt(STATUS_TEXT); - mStatusCorrect = savedInstanceState.getBoolean(STATUS_CORRECT); - mIsSslConn = savedInstanceState.getBoolean(IS_SSL_CONN); - setResultIconAndText(mStatusIcon, mStatusText); - findViewById(R.id.buttonOK).setEnabled(mStatusCorrect); - if (!mStatusCorrect) - iv.setVisibility(View.VISIBLE); - else - iv.setVisibility(View.INVISIBLE); - - String ocVersion = savedInstanceState.getString(OC_VERSION, null); - if (ocVersion != null) - mDiscoveredVersion = new OwnCloudVersion(ocVersion); - - // Getting the state of oAuth2 components. - mOAuth2StatusIcon = savedInstanceState.getInt(OAUTH2_STATUS_ICON); - mOAuth2StatusText = savedInstanceState.getInt(OAUTH2_STATUS_TEXT); - // We set this to true if the rotation happens when the user is validating oAuth2 user_code. - changeViewByOAuth2Check(savedInstanceState.getBoolean(OAUTH2_IS_CHECKED)); - // We store a JSon object with all the data returned from oAuth2 server when we get user_code. - // Is better than store variable by variable. We use String object to serialize from/to it. - try { - if (savedInstanceState.containsKey(OAUTH2_CODE_RESULT)) { - codeResponseJson = new JSONObject(savedInstanceState.getString(OAUTH2_CODE_RESULT)); - } - } catch (JSONException e) { - Log.e(TAG, "onCreate->JSONException: " + e.toString()); - } - // END of getting the state of oAuth2 components. - - // Getting contents of each field. - EditText hostUrl = (EditText)findViewById(R.id.host_URL); - hostUrl.setText(savedInstanceState.getString(HOST_URL_TEXT), TextView.BufferType.EDITABLE); - EditText accountUsername = (EditText)findViewById(R.id.account_username); - accountUsername.setText(savedInstanceState.getString(ACCOUNT_USERNAME), TextView.BufferType.EDITABLE); - EditText accountPassword = (EditText)findViewById(R.id.account_password); - accountPassword.setText(savedInstanceState.getString(ACCOUNT_PASSWORD), TextView.BufferType.EDITABLE); - // END of getting contents of each field + mRefreshButton = (ImageView) findViewById(R.id.refreshButton); + mViewPasswordButton = (ImageView) findViewById(R.id.viewPasswordButton); + mHostUrlInput = (EditText) findViewById(R.id.hostUrlInput); + mUsernameInput = (EditText) findViewById(R.id.account_username); + mPasswordInput = (EditText) findViewById(R.id.account_password); + mOAuthAuthEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_1); + mOAuthTokenEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_2); + mOAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check); + mOkButton = findViewById(R.id.buttonOK); + + /// complete label for 'register account' button + Button b = (Button) findViewById(R.id.account_register); + if (b != null) { + b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name))); + } - } else { + /// bind view elements to listeners + mHostUrlInput.setOnFocusChangeListener(this); + mPasswordInput.setOnFocusChangeListener(this); + + /// initialization + mAccountMgr = AccountManager.get(this); + mNewCapturedUriFromOAuth2Redirection = null; // TODO save? + + if (savedInstanceState == null) { + /// connection state and info mStatusText = mStatusIcon = 0; mStatusCorrect = false; mIsSslConn = false; - String accountName = getIntent().getExtras().getString(PARAM_ACCOUNTNAME); + /// retrieve extras from intent String tokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE); - if (AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN.equals(tokenType)) { - CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check); - oAuth2Check.setChecked(true); - changeViewByOAuth2Check(true); - } + boolean oAuthRequired = AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN.equals(tokenType); + mOAuth2Check.setChecked(oAuthRequired); + changeViewByOAuth2Check(oAuthRequired); - if (accountName != null) { - ((TextView) findViewById(R.id.account_username)).setText(accountName.substring(0, accountName.lastIndexOf('@'))); - tv.setText(accountName.substring(accountName.lastIndexOf('@') + 1)); + Account account = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT); + if (account != null) { + String ocVersion = mAccountMgr.getUserData(account, AccountAuthenticator.KEY_OC_VERSION); + if (ocVersion != null) { + mDiscoveredVersion = new OwnCloudVersion(ocVersion); + } + mHostBaseUrl = mAccountMgr.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL); + mHostUrlInput.setText(mHostBaseUrl); + String userName = account.name.substring(0, account.name.lastIndexOf('@')); + mUsernameInput.setText(userName); } + + } else { + loadSavedInstanceState(savedInstanceState); } - iv.setOnClickListener(this); - iv2.setOnClickListener(this); - tv.setOnFocusChangeListener(this); - tv2.setOnFocusChangeListener(this); - Button b = (Button) findViewById(R.id.account_register); - if (b != null) { - b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name))); + mPasswordInput.setText(""); // clean password to avoid social hacking (disadvantage: password in removed if the device is turned aside) + } + + + /** + * Saves relevant state before {@link #onPause()} + * + * Do NOT save {@link #mNewCapturedUriFromOAuth2Redirection}; it keeps a temporal flag, intended to defer the + * processing of the redirection caught in {@link #onNewIntent(Intent)} until {@link #onResume()} + * + * See {@link #loadSavedInstanceState(Bundle)} + */ + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + /// connection state and info + outState.putInt(KEY_STATUS_TEXT, mStatusText); + outState.putInt(KEY_STATUS_ICON, mStatusIcon); + outState.putBoolean(KEY_STATUS_CORRECT, mStatusCorrect); + outState.putBoolean(KEY_IS_SSL_CONN, mIsSslConn); + + /// server data + if (mDiscoveredVersion != null) + outState.putString(KEY_OC_VERSION, mDiscoveredVersion.toString()); + outState.putString(KEY_HOST_URL_TEXT, mHostBaseUrl); + + // Saving the state of oAuth2 components. + outState.putInt(KEY_OAUTH2_STATUS_ICON, mOAuth2StatusIcon); + outState.putInt(KEY_OAUTH2_STATUS_TEXT, mOAuth2StatusText); + + /* Leave old OAuth flow + if (codeResponseJson != null){ + outState.putString(KEY_OAUTH2_CODE_RESULT, codeResponseJson.toString()); } + */ + } - mNewCapturedUriFromOAuth2Redirection = null; + + /** + * Loads saved state + * + * See {@link #onSaveInstanceState(Bundle)}. + * + * @param savedInstanceState Saved state, as received in {@link #onCreate(Bundle)}. + */ + private void loadSavedInstanceState(Bundle savedInstanceState) { + /// connection state and info + mStatusCorrect = savedInstanceState.getBoolean(KEY_STATUS_CORRECT); + mIsSslConn = savedInstanceState.getBoolean(KEY_IS_SSL_CONN); + mStatusText = savedInstanceState.getInt(KEY_STATUS_TEXT); + mStatusIcon = savedInstanceState.getInt(KEY_STATUS_ICON); + updateOcServerCheckIconAndText(); + + /// UI settings depending upon connection + mOkButton.setEnabled(mStatusCorrect); // TODO really necessary? + if (!mStatusCorrect) + mRefreshButton.setVisibility(View.VISIBLE); // seems that setting visibility is necessary + else + mRefreshButton.setVisibility(View.INVISIBLE); + + /// server data + String ocVersion = savedInstanceState.getString(KEY_OC_VERSION); + if (ocVersion != null) + mDiscoveredVersion = new OwnCloudVersion(ocVersion); + mHostBaseUrl = savedInstanceState.getString(KEY_HOST_URL_TEXT); + + // state of oAuth2 components + mOAuth2StatusIcon = savedInstanceState.getInt(KEY_OAUTH2_STATUS_ICON); + mOAuth2StatusText = savedInstanceState.getInt(KEY_OAUTH2_STATUS_TEXT); + changeViewByOAuth2Check(mOAuth2Check.isChecked()); + + /* Leave old OAuth flow + // We store a JSon object with all the data returned from oAuth2 server when we get user_code. + // Is better than store variable by variable. We use String object to serialize from/to it. + try { + if (savedInstanceState.containsKey(KEY_OAUTH2_CODE_RESULT)) { + codeResponseJson = new JSONObject(savedInstanceState.getString(KEY_OAUTH2_CODE_RESULT)); + } + } catch (JSONException e) { + Log.e(TAG, "onCreate->JSONException: " + e.toString()); + }*/ + // END of getting the state of oAuth2 components. + } + /** + * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION request + * is caught here. + * + * To make this possible, this activity needs to be qualified with android:launchMode = "singleTask" in the + * AndroidManifest.xml file. + */ @Override protected void onNewIntent (Intent intent) { + Log.d(TAG, "onNewIntent()"); Uri data = intent.getData(); - //mNewRedirectUriCaptured = (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI)); if (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI)) { mNewCapturedUriFromOAuth2Redirection = data; } - Log.d(TAG, "onNewIntent()"); - - } - - - @Override - protected void onSaveInstanceState(Bundle outState) { - outState.putInt(STATUS_ICON, mStatusIcon); - outState.putInt(STATUS_TEXT, mStatusText); - outState.putBoolean(STATUS_CORRECT, mStatusCorrect); - if (mDiscoveredVersion != null) - outState.putString(OC_VERSION, mDiscoveredVersion.toString()); - - // Saving the state of oAuth2 components. - outState.putInt(OAUTH2_STATUS_ICON, mOAuth2StatusIcon); - outState.putInt(OAUTH2_STATUS_TEXT, mOAuth2StatusText); - CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check); - outState.putBoolean(OAUTH2_IS_CHECKED, oAuth2Check.isChecked()); - if (codeResponseJson != null){ - outState.putString(OAUTH2_CODE_RESULT, codeResponseJson.toString()); + } + + + /** + * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION, and + * deferred in {@link #onNewIntent(Intent)}, is processed here. + */ + @Override + protected void onResume() { + super.onResume(); + /* LEAVE OLD OAUTH FLOW ; + // (old oauth code) Registering token receiver. We must listening to the service that is pooling to the oAuth server for a token. + if (tokenReceiver == null) { + IntentFilter tokenFilter = new IntentFilter(OAuth2GetTokenService.TOKEN_RECEIVED_MESSAGE); + tokenReceiver = new TokenReceiver(); + this.registerReceiver(tokenReceiver,tokenFilter); + } */ + // (new oauth code) + if (mNewCapturedUriFromOAuth2Redirection != null) { + getOAuth2AccessTokenFromCapturedRedirection(); + } + } + + + @Override protected void onDestroy() { + super.onDestroy(); + + /* LEAVE OLD OAUTH FLOW + // We must stop the service thats it's pooling to oAuth2 server for a token. + Intent tokenService = new Intent(this, OAuth2GetTokenService.class); + stopService(tokenService); + + // We stop listening the result of the pooling service. + if (tokenReceiver != null) { + unregisterReceiver(tokenReceiver); + tokenReceiver = null; + }*/ + + } + + + /** + * Parses the redirection with the response to the GET AUTHORIZATION request to the + * oAuth server and requests for the access token (GET ACCESS TOKEN) + */ + private void getOAuth2AccessTokenFromCapturedRedirection() { + /// Parse data from OAuth redirection + Map responseValues = new HashMap(); + String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery(); + mNewCapturedUriFromOAuth2Redirection = null; + String[] pairs = queryParameters.split("&"); + int i = 0; + String key = ""; + String value = ""; + StringBuilder sb = new StringBuilder(); + while (pairs.length > i) { + int j = 0; + String[] part = pairs[i].split("="); + while (part.length > j) { + String p = part[j]; + if (j == 0) { + key = p; + sb.append(key + " = "); + } else if (j == 1) { + value = p; + responseValues.put(key, value); + sb.append(value + "\n"); + } + + Log.v(TAG, "[" + i + "," + j + "] = " + p); + j++; + } + i++; + } + + /// Updating status widget to OK. + updateOAuth2IconAndText(R.drawable.ic_ok, R.string.auth_connection_established); + + /// Showing the dialog with instructions for the user. + showDialog(DIALOG_OAUTH2_LOGIN_PROGRESS); + + /// GET ACCESS TOKEN to the oAuth server + RemoteOperation operation = new OAuth2GetAccessToken(responseValues); + WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(getString(R.string.oauth_url_endpoint_access)), getApplicationContext()); + operation.execute(client, this, mHandler); + } + + + + /** + * Handles the change of focus on the text inputs for the server URL and the password + */ + public void onFocusChange(View view, boolean hasFocus) { + if (view.getId() == R.id.hostUrlInput) { + onUrlInputFocusChanged((TextView) view, hasFocus); + + } else if (view.getId() == R.id.account_password) { + onPasswordFocusChanged((TextView) view, hasFocus); + } + } + + + /** + * Handles changes in focus on the text input for the server URL. + * + * IMPORTANT ENTRY POINT 2: When (!hasFocus), user wrote the server URL and changed to + * other field. The operation to check the existence of the server in the entered URL is + * started. + * + * When hasFocus: user 'comes back' to write again the server URL. + * + * @param hostInput TextView with the URL input field receiving the change of focus. + * @param hasFocus 'True' if focus is received, 'false' if is lost + */ + private void onUrlInputFocusChanged(TextView hostInput, boolean hasFocus) { + if (!hasFocus) { + String uri = hostInput.getText().toString().trim(); + if (uri.length() != 0) { + mStatusText = R.string.auth_testing_connection; + mStatusIcon = R.drawable.progress_small; + updateOcServerCheckIconAndText(); + /** TODO cancel previous connection check if the user tries to ammend a wrong URL + if(mConnChkOperation != null) { + mConnChkOperation.cancel(); + } */ + mOcServerChkOperation = new OwnCloudServerCheckOperation(uri, this); + WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this); + mHostBaseUrl = ""; + mDiscoveredVersion = null; + mOperationThread = mOcServerChkOperation.execute(client, this, mHandler); + } else { + mRefreshButton.setVisibility(View.INVISIBLE); + mStatusText = 0; + mStatusIcon = 0; + updateOcServerCheckIconAndText(); + } + } else { + // avoids that the 'connect' button can be clicked if the test was previously passed + mOkButton.setEnabled(false); + } + } + + + /** + * Handles changes in focus on the text input for the password (basic authorization). + * + * When (hasFocus), the button to toggle password visibility is shown. + * + * When (!hasFocus), the button is made invisible and the password is hidden. + * + * @param passwordInput TextView with the password input field receiving the change of focus. + * @param hasFocus 'True' if focus is received, 'false' if is lost + */ + private void onPasswordFocusChanged(TextView passwordInput, boolean hasFocus) { + if (hasFocus) { + mViewPasswordButton.setVisibility(View.VISIBLE); + } else { + int input_type = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD; + passwordInput.setInputType(input_type); + mViewPasswordButton.setVisibility(View.INVISIBLE); + } + } + + + + /** + * Cancels the authenticator activity + * + * IMPORTANT ENTRY POINT 3: Never underestimate the importance of cancellation + * + * This method is bound in the layout/acceoun_setup.xml resource file. + * + * @param view Cancel button + */ + public void onCancelClick(View view) { + setResult(RESULT_CANCELED); // TODO review how is this related to AccountAuthenticator + finish(); + } + + + + /** + * Checks the credentials of the user in the root of the ownCloud server + * before creating a new local account. + * + * For basic authorization, a check of existence of the root folder is + * performed. + * + * For OAuth, starts the flow to get an access token; the credentials test + * is postponed until it is available. + * + * IMPORTANT ENTRY POINT 4 + * + * @param view OK button + */ + public void onOkClick(View view) { + // this check should be unnecessary + if (mDiscoveredVersion == null || !mDiscoveredVersion.isVersionValid() || mHostBaseUrl == null || mHostBaseUrl.length() == 0) { + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_wtf_reenter_URL; + updateOcServerCheckIconAndText(); + mOkButton.setEnabled(false); + Log.wtf(TAG, "The user was allowed to click 'connect' to an unchecked server!!"); + return; + } + + if (mOAuth2Check.isChecked()) { + startOauthorization(); + + } else { + checkBasicAuthorization(); + } + } + + + /** + * Tests the credentials entered by the user performing a check of existence on + * the root folder of the ownCloud server. + */ + private void checkBasicAuthorization() { + /// get the path to the root folder through WebDAV from the version server + String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, false); + + /// get basic credentials entered by user + String username = mUsernameInput.getText().toString(); + String password = mPasswordInput.getText().toString(); + + /// be gentle with the user + showDialog(DIALOG_LOGIN_PROGRESS); + + /// test credentials accessing the root folder + mAuthCheckOperation = new ExistenceCheckOperation("", this, false); + WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this); + client.setBasicCredentials(username, password); + mOperationThread = mAuthCheckOperation.execute(client, this, mHandler); + } + + + /** + * Starts the OAuth 'grant type' flow to get an access token, with + * a GET AUTHORIZATION request to the BUILT-IN authorization server. + */ + private void startOauthorization() { + // be gentle with the user + updateOAuth2IconAndText(R.drawable.progress_small, R.string.oauth_login_connection); + + // GET AUTHORIZATION request + /* + mOAuth2GetCodeRunnable = new OAuth2GetAuthorizationToken(, this); + mOAuth2GetCodeRunnable.setListener(this, mHandler); + mOAuth2GetCodeThread = new Thread(mOAuth2GetCodeRunnable); + mOAuth2GetCodeThread.start(); + */ + + //if (mGrantType.equals(OAuth2Context.OAUTH2_AUTH_CODE_GRANT_TYPE)) { + Uri uri = Uri.parse(getString(R.string.oauth_url_endpoint_auth)); + Uri.Builder uriBuilder = uri.buildUpon(); + uriBuilder.appendQueryParameter(OAuth2Context.CODE_RESPONSE_TYPE, OAuth2Context.OAUTH2_CODE_RESPONSE_TYPE); + uriBuilder.appendQueryParameter(OAuth2Context.CODE_REDIRECT_URI, OAuth2Context.MY_REDIRECT_URI); + uriBuilder.appendQueryParameter(OAuth2Context.CODE_CLIENT_ID, OAuth2Context.OAUTH2_F_CLIENT_ID); + uriBuilder.appendQueryParameter(OAuth2Context.CODE_SCOPE, OAuth2Context.OAUTH2_F_SCOPE); + //uriBuilder.appendQueryParameter(OAuth2Context.CODE_STATE, whateverwewant); + uri = uriBuilder.build(); + Log.d(TAG, "Starting browser to view " + uri.toString()); + Intent i = new Intent(Intent.ACTION_VIEW, uri); + startActivity(i); + //} + } + + + /** + * Callback method invoked when a RemoteOperation executed by this Activity finishes. + * + * Dispatches the operation flow to the right method. + */ + @Override + public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { + + if (operation instanceof OwnCloudServerCheckOperation) { + onOcServerCheckFinish((OwnCloudServerCheckOperation) operation, result); + + } else if (operation instanceof OAuth2GetAccessToken) { + onGetOAuthAccessTokenFinish((OAuth2GetAccessToken)operation, result); + + } else if (operation instanceof ExistenceCheckOperation) { + onAuthorizationCheckFinish((ExistenceCheckOperation)operation, result); + + } + } + + + /** + * Processes the result of the server check performed when the user finishes the enter of the + * server URL. + * + * @param operation Server check performed. + * @param result Result of the check. + */ + private void onOcServerCheckFinish(OwnCloudServerCheckOperation operation, RemoteOperationResult result) { + /// update status connection icon and text + mStatusText = mStatusIcon = 0; + mStatusCorrect = false; + + switch (result.getCode()) { + case OK_SSL: + mIsSslConn = true; + mStatusIcon = android.R.drawable.ic_secure; + mStatusText = R.string.auth_secure_connection; + mStatusCorrect = true; + break; + + case OK_NO_SSL: + case OK: + mIsSslConn = false; + mStatusCorrect = true; + if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) { + mStatusText = R.string.auth_connection_established; + mStatusIcon = R.drawable.ic_ok; + } else { + mStatusText = R.string.auth_nossl_plain_ok_title; + mStatusIcon = android.R.drawable.ic_partial_secure; + } + break; + + /// very special case (TODO: move to a common place for all the remote operations) + case SSL_RECOVERABLE_PEER_UNVERIFIED: + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_ssl_unverified_server_title; + mLastSslUntrustedServerResult = result; + showDialog(DIALOG_SSL_VALIDATOR); + break; + + case BAD_OC_VERSION: + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_bad_oc_version_title; + break; + case WRONG_CONNECTION: + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_wrong_connection_title; + break; + case TIMEOUT: + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_timeout_title; + break; + case INCORRECT_ADDRESS: + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_incorrect_address_title; + break; + + case SSL_ERROR: + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_ssl_general_error_title; + break; + + case HOST_NOT_AVAILABLE: + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_unknown_host_title; + break; + case NO_NETWORK_CONNECTION: + mStatusIcon = R.drawable.no_network; + mStatusText = R.string.auth_no_net_conn_title; + break; + case INSTANCE_NOT_CONFIGURED: + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_not_configured_title; + break; + case FILE_NOT_FOUND: + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_incorrect_path_title; + break; + case UNHANDLED_HTTP_CODE: + case UNKNOWN_ERROR: + mStatusIcon = R.drawable.common_error; + mStatusText = R.string.auth_unknown_error_title; + break; + default: + Log.e(TAG, "Incorrect connection checker result type: " + result.getHttpCode()); + } + updateOcServerCheckIconAndText(); + + /// update the visibility of the 'retry connection' button + if (!mStatusCorrect) + mRefreshButton.setVisibility(View.VISIBLE); + else + mRefreshButton.setVisibility(View.INVISIBLE); + + /// retrieve discovered version and normalize server URL + mDiscoveredVersion = operation.getDiscoveredVersion(); + mHostBaseUrl = mHostUrlInput.getText().toString().trim(); + if (!mHostBaseUrl.toLowerCase().startsWith("http://") && + !mHostBaseUrl.toLowerCase().startsWith("https://")) { + + if (mIsSslConn) { + mHostBaseUrl = "https://" + mHostBaseUrl; + } else { + mHostBaseUrl = "http://" + mHostBaseUrl; + } + + } + if (mHostBaseUrl.endsWith("/")) + mHostBaseUrl = mHostBaseUrl.substring(0, mHostBaseUrl.length() - 1); + + /// allow or not the user try to access the server + mOkButton.setEnabled(mStatusCorrect); + } + + + /** + * Processes the result of the request for and access token send + * to an OAuth authorization server. + * + * @param operation Operation performed requesting the access token. + * @param result Result of the operation. + */ + private void onGetOAuthAccessTokenFinish(OAuth2GetAccessToken operation, RemoteOperationResult result) { + try { + dismissDialog(DIALOG_OAUTH2_LOGIN_PROGRESS); + } catch (IllegalArgumentException e) { + // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens + } + + String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, false); + if (result.isSuccess() && webdav_path != null) { + /// be gentle with the user + showDialog(DIALOG_LOGIN_PROGRESS); + + /// time to test the retrieved access token on the ownCloud server + mOAuthAccessToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Context.KEY_ACCESS_TOKEN); + Log.d(TAG, "Got ACCESS TOKEN: " + mOAuthAccessToken); + mAuthCheckOperation = new ExistenceCheckOperation("", this, false); + WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this); + client.setBearerCredentials(mOAuthAccessToken); + mAuthCheckOperation.execute(client, this, mHandler); + + } else { + if (webdav_path != null) { + mOAuthAuthEndpointText.setError("A valid authorization could not be obtained"); + } else { + mOAuthAuthEndpointText.setError(getString(R.string.auth_bad_oc_version_title)); // should never happen + } + } + } + + + /** + * Processes the result of the access check performed to try the user credentials. + * + * Creates a new account through the AccountManager. + * + * @param operation Access check performed. + * @param result Result of the operation. + */ + private void onAuthorizationCheckFinish(ExistenceCheckOperation operation, RemoteOperationResult result) { + try { + dismissDialog(DIALOG_LOGIN_PROGRESS); + } catch (IllegalArgumentException e) { + // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens + } + + boolean isOAuth = mOAuth2Check.isChecked(); + + if (result.isSuccess()) { + Log.d(TAG, "Successful access - time to save the account"); + + /// create and save new ownCloud account + Uri uri = Uri.parse(mHostBaseUrl); + String username = isOAuth ? + "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong() : + mUsernameInput.getText().toString().trim(); + // TODO a better way to set an account name + String accountName = username + "@" + uri.getHost(); + if (uri.getPort() >= 0) { + accountName += ":" + uri.getPort(); + } + Account account = new Account(accountName, AccountAuthenticator.ACCOUNT_TYPE); + AccountManager accManager = AccountManager.get(this); + if (isOAuth) { + accManager.addAccountExplicitly(account, "", null); // with our implementation, the password is never input in the app + } else { + accManager.addAccountExplicitly(account, mPasswordInput.getText().toString(), null); + } + + /// add the new account as default in preferences, if there is none already + Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this); + if (defaultAccount == null) { + SharedPreferences.Editor editor = PreferenceManager + .getDefaultSharedPreferences(this).edit(); + editor.putString("select_oc_account", accountName); + editor.commit(); + } + + + /// prepare result to return to the Authenticator + // TODO check again what the Authenticator makes with it; probably has the same effect as addAccountExplicitly, but it's not well done + final Intent intent = new Intent(); // TODO check if the intent can be retrieved from getIntent(), passed from AccountAuthenticator + intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountAuthenticator.ACCOUNT_TYPE); + intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name); + if (!isOAuth) + intent.putExtra(AccountManager.KEY_AUTHTOKEN, AccountAuthenticator.ACCOUNT_TYPE); // TODO check this; not sure it's right; maybe + intent.putExtra(AccountManager.KEY_USERDATA, username); + if (isOAuth) { + accManager.setAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, mOAuthAccessToken); + } + /// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA + accManager.setUserData(account, AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString()); + accManager.setUserData(account, AccountAuthenticator.KEY_OC_BASE_URL, mHostBaseUrl); + if (isOAuth) + accManager.setUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE"); // TODO this flag should be unnecessary + + setAccountAuthenticatorResult(intent.getExtras()); + setResult(RESULT_OK, intent); + + /// immediately request for the synchronization of the new account + Bundle bundle = new Bundle(); + bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + ContentResolver.requestSync(account, AccountAuthenticator.AUTHORITY, bundle); + + finish(); + + } else { + if (!isOAuth) { + mUsernameInput.setError(result.getLogMessage() + " "); + // the extra spaces are a workaround for an ugly bug: + // 1. insert wrong credentials and connect + // 2. put the focus on the user name field with using hardware controls (don't touch the screen); the error is shown UNDER the field + // 3. touch the user name field; the software keyboard appears; the error popup is moved OVER the field and SHRINKED in width, losing the last word + // Seen, at least, in Android 2.x devices + + } else { + mOAuthAuthEndpointText.setError(result.getLogMessage() + " "); + } + Log.d(TAG, "Access failed: " + result.getLogMessage()); + } + } + + + + /** + * {@inheritDoc} + * + * Necessary to update the contents of the SSL Dialog + * + * TODO move to some common place for all possible untrusted SSL failures + */ + @Override + protected void onPrepareDialog(int id, Dialog dialog, Bundle args) { + switch (id) { + case DIALOG_LOGIN_PROGRESS: + case DIALOG_CERT_NOT_SAVED: + case DIALOG_OAUTH2_LOGIN_PROGRESS: + break; + case DIALOG_SSL_VALIDATOR: { + ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult); + break; + } + default: + Log.e(TAG, "Incorrect dialog called with id = " + id); } - // END of saving the state of oAuth2 components. - - // Saving contents of each field. - outState.putString(HOST_URL_TEXT,((TextView) findViewById(R.id.host_URL)).getText().toString().trim()); - outState.putString(ACCOUNT_USERNAME,((TextView) findViewById(R.id.account_username)).getText().toString().trim()); - outState.putString(ACCOUNT_PASSWORD,((TextView) findViewById(R.id.account_password)).getText().toString().trim()); - - super.onSaveInstanceState(outState); } + + /** + * {@inheritDoc} + */ @Override protected Dialog onCreateDialog(int id) { Dialog dialog = null; switch (id) { case DIALOG_LOGIN_PROGRESS: { + /// simple progress dialog ProgressDialog working_dialog = new ProgressDialog(this); - working_dialog.setMessage(getResources().getString( - R.string.auth_trying_to_login)); + working_dialog.setMessage(getResources().getString(R.string.auth_trying_to_login)); working_dialog.setIndeterminate(true); working_dialog.setCancelable(true); working_dialog .setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { + /// TODO study if this is enough Log.i(TAG, "Login canceled"); - if (mAuthThread != null) { - mAuthThread.interrupt(); + if (mOperationThread != null) { + mOperationThread.interrupt(); finish(); } } @@ -288,20 +884,22 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity dialog = working_dialog; break; } - // oAuth2 dialog. We show here to the user the URL and user_code that the user must validate in a web browser. - case OAUTH2_LOGIN_PROGRESS: { + case DIALOG_OAUTH2_LOGIN_PROGRESS: { + /// oAuth2 dialog. We show here to the user the URL and user_code that the user must validate in a web browser. - OLD! + // TODO optimize this dialog ProgressDialog working_dialog = new ProgressDialog(this); + /* Leave the old OAuth flow try { - if (codeResponseJson != null && codeResponseJson.has(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL)) { + if (mCodeResponseJson != null && mCodeResponseJson.has(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL)) { working_dialog.setMessage(String.format(getString(R.string.oauth_code_validation_message), - codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL), - codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE))); - } else { - working_dialog.setMessage(String.format("Getting authorization")); - } + mCodeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL), + mCodeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE))); + } else {*/ + working_dialog.setMessage(String.format("Getting authorization")); + /*} } catch (JSONException e) { Log.e(TAG, "onCreateDialog->JSONException: " + e.toString()); - } + }*/ working_dialog.setIndeterminate(true); working_dialog.setCancelable(true); working_dialog @@ -309,21 +907,23 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity @Override public void onCancel(DialogInterface dialog) { Log.i(TAG, "Login canceled"); - if (mOAuth2GetCodeThread != null) { + /*if (mOAuth2GetCodeThread != null) { mOAuth2GetCodeThread.interrupt(); finish(); - } - if (tokenReceiver != null) { + } */ + /*if (tokenReceiver != null) { unregisterReceiver(tokenReceiver); tokenReceiver = null; finish(); - } + }*/ + finish(); } }); dialog = working_dialog; break; } case DIALOG_SSL_VALIDATOR: { + /// TODO start to use new dialog interface, at least for this (it is a FragmentDialog already) dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this); break; } @@ -346,414 +946,126 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity return dialog; } - @Override - protected void onPrepareDialog(int id, Dialog dialog, Bundle args) { - switch (id) { - case DIALOG_LOGIN_PROGRESS: - case DIALOG_CERT_NOT_SAVED: - case OAUTH2_LOGIN_PROGRESS: - break; - case DIALOG_SSL_VALIDATOR: { - ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult); - break; - } - default: - Log.e(TAG, "Incorrect dialog called with id = " + id); - } - } - - @Override - protected void onResume() { - Log.d(TAG, "onResume() start"); - // (old oauth code) Registering token receiver. We must listening to the service that is pooling to the oAuth server for a token. - if (tokenReceiver == null) { - IntentFilter tokenFilter = new IntentFilter(OAuth2GetTokenService.TOKEN_RECEIVED_MESSAGE); - tokenReceiver = new TokenReceiver(); - this.registerReceiver(tokenReceiver,tokenFilter); - } - // (new oauth code) - /*if (mNewRedirectUriCaptured) { - mNewRedirectUriCaptured = false;*/ - if (mNewCapturedUriFromOAuth2Redirection != null) { - getOAuth2AccessTokenFromCapturedRedirection(); - - } - super.onResume(); - } - - @Override - protected void onPause() { - Log.d(TAG, "onPause() start"); - super.onPause(); - } - - - public void onAuthenticationResult(boolean success, String message) { - if (success) { - TextView username_text = (TextView) findViewById(R.id.account_username), password_text = (TextView) findViewById(R.id.account_password); - - URL url; - try { - url = new URL(message); - } catch (MalformedURLException e) { - // should never happen - Log.e(getClass().getName(), "Malformed URL: " + message); - return; - } - - String username = username_text.getText().toString().trim(); - String accountName = username + "@" + url.getHost(); - if (url.getPort() >= 0) { - accountName += ":" + url.getPort(); - } - Account account = new Account(accountName, - AccountAuthenticator.ACCOUNT_TYPE); - AccountManager accManager = AccountManager.get(this); - accManager.addAccountExplicitly(account, password_text.getText() - .toString(), null); - - // Add this account as default in the preferences, if there is none - // already - Account defaultAccount = AccountUtils - .getCurrentOwnCloudAccount(this); - if (defaultAccount == null) { - SharedPreferences.Editor editor = PreferenceManager - .getDefaultSharedPreferences(this).edit(); - editor.putString("select_oc_account", accountName); - editor.commit(); - } - - final Intent intent = new Intent(); - intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, - AccountAuthenticator.ACCOUNT_TYPE); - intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name); - intent.putExtra(AccountManager.KEY_AUTHTOKEN, - AccountAuthenticator.ACCOUNT_TYPE); - intent.putExtra(AccountManager.KEY_USERDATA, username); - - accManager.setUserData(account, AccountAuthenticator.KEY_OC_URL, - url.toString()); - accManager.setUserData(account, - AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString()); - - accManager.setUserData(account, - AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl); - - setAccountAuthenticatorResult(intent.getExtras()); - setResult(RESULT_OK, intent); - Bundle bundle = new Bundle(); - bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); - //getContentResolver().startSync(ProviderTableMeta.CONTENT_URI, - // bundle); - ContentResolver.requestSync(account, "org.owncloud", bundle); - - /* - * if - * (mConnChkRunnable.getDiscoveredVersion().compareTo(OwnCloudVersion - * .owncloud_v2) >= 0) { Intent i = new Intent(this, - * ExtensionsAvailableActivity.class); startActivity(i); } - */ - - finish(); - } else { - try { - dismissDialog(DIALOG_LOGIN_PROGRESS); - } catch (IllegalArgumentException e) { - // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens - } - TextView tv = (TextView) findViewById(R.id.account_username); - tv.setError(message + " "); // the extra spaces are a workaround for an ugly bug: - // 1. insert wrong credentials and connect - // 2. put the focus on the user name field with using hardware controls (don't touch the screen); the error is shown UNDER the field - // 3. touch the user name field; the software keyboard appears; the error popup is moved OVER the field and SHRINKED in width, losing the last word - // Seen, at least, in Android 2.x devices - } - } - public void onCancelClick(View view) { - setResult(RESULT_CANCELED); - finish(); - } - - public void onOkClick(View view) { - String prefix = ""; - String url = ((TextView) findViewById(R.id.host_URL)).getText() - .toString().trim(); - if (mIsSslConn) { - prefix = "https://"; - } else { - prefix = "http://"; - } - if (url.toLowerCase().startsWith("http://") - || url.toLowerCase().startsWith("https://")) { - prefix = ""; - } - CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check); - if (oAuth2Check != null && oAuth2Check.isChecked()) { - startOauthorization(); - - } else { - continueConnection(prefix); - } - } - private void startOauthorization() { - // We start a thread to get an authorization code from the oAuth2 server. - setOAuth2ResultIconAndText(R.drawable.progress_small, R.string.oauth_login_connection); - mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(OAuth2Context.OAUTH2_F_AUTHORIZATION_ENDPOINT_URL, this); - //mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(OAuth2Context.OAUTH2_G_DEVICE_GETCODE_URL, this); - mOAuth2GetCodeRunnable.setListener(this, mHandler); - mOAuth2GetCodeThread = new Thread(mOAuth2GetCodeRunnable); - mOAuth2GetCodeThread.start(); - } - + /** + * Starts and activity to open the 'new account' page in the ownCloud web site + * + * @param view 'Account register' button + */ public void onRegisterClick(View view) { Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register))); setResult(RESULT_CANCELED); startActivity(register); } - private void continueConnection(String prefix) { - String url = ((TextView) findViewById(R.id.host_URL)).getText() - .toString().trim(); - String username = ((TextView) findViewById(R.id.account_username)) - .getText().toString(); - String password = ((TextView) findViewById(R.id.account_password)) - .getText().toString(); - if (url.endsWith("/")) - url = url.substring(0, url.length() - 1); - - URL uri = null; - mDiscoveredVersion = mConnChkRunnable.getDiscoveredVersion(); - String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, false); - - if (webdav_path == null) { - onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title)); - return; - } - - try { - mBaseUrl = prefix + url; - String url_str = prefix + url + webdav_path; - uri = new URL(url_str); - } catch (MalformedURLException e) { - // should never happen - onAuthenticationResult(false, getString(R.string.auth_incorrect_address_title)); - return; - } - - showDialog(DIALOG_LOGIN_PROGRESS); - mAuthRunnable = new AuthenticationRunnable(uri, username, password, this); - mAuthRunnable.setOnAuthenticationResultListener(this, mHandler); - mAuthThread = new Thread(mAuthRunnable); - mAuthThread.start(); - } - - @Override - public void onConnectionCheckResult(ResultType type) { - mStatusText = mStatusIcon = 0; - mStatusCorrect = false; - String t_url = ((TextView) findViewById(R.id.host_URL)).getText() - .toString().trim().toLowerCase(); - - switch (type) { - case OK_SSL: - mIsSslConn = true; - mStatusIcon = android.R.drawable.ic_secure; - mStatusText = R.string.auth_secure_connection; - mStatusCorrect = true; - break; - case OK_NO_SSL: - mIsSslConn = false; - mStatusCorrect = true; - if (t_url.startsWith("http://") ) { - mStatusText = R.string.auth_connection_established; - mStatusIcon = R.drawable.ic_ok; - } else { - mStatusText = R.string.auth_nossl_plain_ok_title; - mStatusIcon = android.R.drawable.ic_partial_secure; - } - break; - case BAD_OC_VERSION: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_bad_oc_version_title; - break; - case WRONG_CONNECTION: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_wrong_connection_title; - break; - case TIMEOUT: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_timeout_title; - break; - case INCORRECT_ADDRESS: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_incorrect_address_title; - break; - case SSL_UNVERIFIED_SERVER: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_ssl_unverified_server_title; - break; - case SSL_INIT_ERROR: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_ssl_general_error_title; - break; - case HOST_NOT_AVAILABLE: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_unknown_host_title; - break; - case NO_NETWORK_CONNECTION: - mStatusIcon = R.drawable.no_network; - mStatusText = R.string.auth_no_net_conn_title; - break; - case INSTANCE_NOT_CONFIGURED: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_not_configured_title; - break; - case UNKNOWN_ERROR: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_unknown_error_title; - break; - case FILE_NOT_FOUND: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_incorrect_path_title; - break; - default: - Log.e(TAG, "Incorrect connection checker result type: " + type); - } - setResultIconAndText(mStatusIcon, mStatusText); - if (!mStatusCorrect) - findViewById(R.id.refreshButton).setVisibility(View.VISIBLE); - else - findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE); - findViewById(R.id.buttonOK).setEnabled(mStatusCorrect); - } - - public void onFocusChange(View view, boolean hasFocus) { - if (view.getId() == R.id.host_URL) { - if (!hasFocus) { - TextView tv = ((TextView) findViewById(R.id.host_URL)); - String uri = tv.getText().toString().trim(); - if (uri.length() != 0) { - setResultIconAndText(R.drawable.progress_small, - R.string.auth_testing_connection); - //mConnChkRunnable = new ConnectionCheckerRunnable(uri, this); - mConnChkRunnable = new ConnectionCheckOperation(uri, this); - //mConnChkRunnable.setListener(this, mHandler); - //mAuthThread = new Thread(mConnChkRunnable); - //mAuthThread.start(); - WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this); - mDiscoveredVersion = null; - mAuthThread = mConnChkRunnable.execute(client, this, mHandler); - } else { - findViewById(R.id.refreshButton).setVisibility( - View.INVISIBLE); - setResultIconAndText(0, 0); - } - } else { - // avoids that the 'connect' button can be clicked if the test was previously passed - findViewById(R.id.buttonOK).setEnabled(false); - } - } else if (view.getId() == R.id.account_password) { - ImageView iv = (ImageView) findViewById(R.id.viewPassword); - if (hasFocus) { - iv.setVisibility(View.VISIBLE); - } else { - TextView v = (TextView) findViewById(R.id.account_password); - int input_type = InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_VARIATION_PASSWORD; - v.setInputType(input_type); - iv.setVisibility(View.INVISIBLE); - } - } - } - - private void setResultIconAndText(int drawable_id, int text_id) { + + /** + * Updates the content and visibility state of the icon and text associated + * to the last check on the ownCloud server. + */ + private void updateOcServerCheckIconAndText() { ImageView iv = (ImageView) findViewById(R.id.action_indicator); TextView tv = (TextView) findViewById(R.id.status_text); - if (drawable_id == 0 && text_id == 0) { + if (mStatusIcon == 0 && mStatusText == 0) { iv.setVisibility(View.INVISIBLE); tv.setVisibility(View.INVISIBLE); } else { - iv.setImageResource(drawable_id); - tv.setText(text_id); + iv.setImageResource(mStatusIcon); + tv.setText(mStatusText); iv.setVisibility(View.VISIBLE); tv.setVisibility(View.VISIBLE); } } - @Override - public void onClick(View v) { - if (v.getId() == R.id.refreshButton) { - onFocusChange(findViewById(R.id.host_URL), false); - } else if (v.getId() == R.id.viewPassword) { - EditText view = (EditText) findViewById(R.id.account_password); - int selectionStart = view.getSelectionStart(); - int selectionEnd = view.getSelectionEnd(); - int input_type = view.getInputType(); - if ((input_type & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) { - input_type = InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_VARIATION_PASSWORD; - } else { - input_type = InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; - } - view.setInputType(input_type); - view.setSelection(selectionStart, selectionEnd); - } + + /** + * Called when the refresh button in the input field for ownCloud host is clicked. + * + * Performs a new check on the URL in the input field. + * + * @param view Refresh 'button' + */ + public void onRefreshClick(View view) { + onFocusChange(mRefreshButton, false); } - @Override protected void onDestroy() { - // We must stop the service thats it's pooling to oAuth2 server for a token. - Intent tokenService = new Intent(this, OAuth2GetTokenService.class); - stopService(tokenService); - - // We stop listening the result of the pooling service. - if (tokenReceiver != null) { - unregisterReceiver(tokenReceiver); - tokenReceiver = null; - finish(); + + /** + * Called when the eye icon in the password field is clicked. + * + * Toggles the visibility of the password in the field. + * + * @param view 'View password' 'button' + */ + public void onViewPasswordClick(View view) { + int selectionStart = mPasswordInput.getSelectionStart(); + int selectionEnd = mPasswordInput.getSelectionEnd(); + int input_type = mPasswordInput.getInputType(); + if ((input_type & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) { + input_type = InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_PASSWORD; + } else { + input_type = InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; } - - super.onDestroy(); + mPasswordInput.setInputType(input_type); + mPasswordInput.setSelection(selectionStart, selectionEnd); } - // Controlling the oAuth2 checkbox on the activity: hide and show widgets. - public void onOff_check_Click(View view) { + + /** + * Called when the checkbox for OAuth authorization is clicked. + * + * Hides or shows the input fields for user & password. + * + * @param view 'View password' 'button' + */ + public void onCheckClick(View view) { CheckBox oAuth2Check = (CheckBox)view; changeViewByOAuth2Check(oAuth2Check.isChecked()); } + /** + * Changes the visibility of input elements depending upon the kind of authorization + * chosen by the user: basic or OAuth + * + * @param checked 'True' when OAuth is selected. + */ public void changeViewByOAuth2Check(Boolean checked) { - EditText oAuth2Url = (EditText) findViewById(R.id.oAuth_URL); - EditText accountUsername = (EditText) findViewById(R.id.account_username); - EditText accountPassword = (EditText) findViewById(R.id.account_password); - ImageView viewPassword = (ImageView) findViewById(R.id.viewPassword); ImageView auth2ActionIndicator = (ImageView) findViewById(R.id.auth2_action_indicator); TextView oauth2StatusText = (TextView) findViewById(R.id.oauth2_status_text); if (checked) { - oAuth2Url.setVisibility(View.VISIBLE); - accountUsername.setVisibility(View.GONE); - accountPassword.setVisibility(View.GONE); - viewPassword.setVisibility(View.GONE); + mOAuthAuthEndpointText.setVisibility(View.VISIBLE); + mOAuthTokenEndpointText.setVisibility(View.VISIBLE); + mUsernameInput.setVisibility(View.GONE); + mPasswordInput.setVisibility(View.GONE); + mViewPasswordButton.setVisibility(View.GONE); auth2ActionIndicator.setVisibility(View.INVISIBLE); oauth2StatusText.setVisibility(View.INVISIBLE); } else { - oAuth2Url.setVisibility(View.GONE); - accountUsername.setVisibility(View.VISIBLE); - accountPassword.setVisibility(View.VISIBLE); - viewPassword.setVisibility(View.INVISIBLE); + mOAuthAuthEndpointText.setVisibility(View.GONE); + mOAuthTokenEndpointText.setVisibility(View.GONE); + mUsernameInput.setVisibility(View.VISIBLE); + mPasswordInput.setVisibility(View.VISIBLE); + mViewPasswordButton.setVisibility(View.INVISIBLE); auth2ActionIndicator.setVisibility(View.GONE); oauth2StatusText.setVisibility(View.GONE); } } - // Controlling the oAuth2 result of server connection. - private void setOAuth2ResultIconAndText(int drawable_id, int text_id) { + /** + * Updates the content and visibility state of the icon and text associated + * to the interactions with the OAuth authorization server. + * + * @param drawable_id Resource id for the icon. + * @param text_id Resource id for the text. + */ + private void updateOAuth2IconAndText(int drawable_id, int text_id) { ImageView iv = (ImageView) findViewById(R.id.auth2_action_indicator); TextView tv = (TextView) findViewById(R.id.oauth2_status_text); @@ -768,17 +1080,18 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } } + /* Leave the old OAuth flow // Results from the first call to oAuth2 server : getting the user_code and verification_url. @Override public void onOAuth2GetCodeResult(ResultOAuthType type, JSONObject responseJson) { if ((type == ResultOAuthType.OK_SSL)||(type == ResultOAuthType.OK_NO_SSL)) { - codeResponseJson = responseJson; - if (codeResponseJson != null) { + mCodeResponseJson = responseJson; + if (mCodeResponseJson != null) { getOAuth2AccessTokenFromJsonResponse(); } // else - nothing to do here - wait for callback !!! } else if (type == ResultOAuthType.HOST_NOT_AVAILABLE) { - setOAuth2ResultIconAndText(R.drawable.common_error, R.string.oauth_connection_url_unavailable); + updateOAuth2IconAndText(R.drawable.common_error, R.string.oauth_connection_url_unavailable); } } @@ -791,27 +1104,27 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity int expiresIn = -1; int interval = -1; - Log.d(TAG, "ResponseOAuth2->" + codeResponseJson.toString()); + Log.d(TAG, "ResponseOAuth2->" + mCodeResponseJson.toString()); try { // We get data that we must show to the user or we will use internally. - verificationUrl = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL); - userCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE); - expiresIn = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_EXPIRES_IN); + verificationUrl = mCodeResponseJson.getString(OAuth2GetAuthorizationToken.CODE_VERIFICATION_URL); + userCode = mCodeResponseJson.getString(OAuth2GetAuthorizationToken.CODE_USER_CODE); + expiresIn = mCodeResponseJson.getInt(OAuth2GetAuthorizationToken.CODE_EXPIRES_IN); // And we get data that we must use to get a token. - deviceCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_DEVICE_CODE); - interval = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_INTERVAL); + deviceCode = mCodeResponseJson.getString(OAuth2GetAuthorizationToken.CODE_DEVICE_CODE); + interval = mCodeResponseJson.getInt(OAuth2GetAuthorizationToken.CODE_INTERVAL); } catch (JSONException e) { Log.e(TAG, "Exception accesing data in Json object" + e.toString()); } // Updating status widget to OK. - setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established); + updateOAuth2IconAndText(R.drawable.ic_ok, R.string.auth_connection_established); // Showing the dialog with instructions for the user. - showDialog(OAUTH2_LOGIN_PROGRESS); + showDialog(DIALOG_OAUTH2_LOGIN_PROGRESS); // Loggin all the data. Log.d(TAG, "verificationUrl->" + verificationUrl); @@ -834,295 +1147,39 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } } + */ - private void getOAuth2AccessTokenFromCapturedRedirection() { - Map responseValues = new HashMap(); - //String queryParameters = getIntent().getData().getQuery(); - String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery(); - mNewCapturedUriFromOAuth2Redirection = null; - - Log.v(TAG, "Queryparameters (Code) = " + queryParameters); - - String[] pairs = queryParameters.split("&"); - Log.v(TAG, "Pairs (Code) = " + pairs.toString()); - - int i = 0; - String key = ""; - String value = ""; - - StringBuilder sb = new StringBuilder(); - - while (pairs.length > i) { - int j = 0; - String[] part = pairs[i].split("="); - - while (part.length > j) { - String p = part[j]; - if (j == 0) { - key = p; - sb.append(key + " = "); - } else if (j == 1) { - value = p; - responseValues.put(key, value); - sb.append(value + "\n"); - } - - Log.v(TAG, "[" + i + "," + j + "] = " + p); - j++; - } - i++; - } - - - // Updating status widget to OK. - setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established); - - // Showing the dialog with instructions for the user. - showDialog(OAUTH2_LOGIN_PROGRESS); - - // - RemoteOperation operation = new GetOAuth2AccessToken(responseValues); - WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(OAuth2Context.OAUTH2_F_TOKEN_ENDPOINT_URL), getApplicationContext()); - operation.execute(client, this, mHandler); - } - - - + /* Leave the old OAuth flow // We get data from the oAuth2 token service with this broadcast receiver. private class TokenReceiver extends BroadcastReceiver { /** * The token is received. * @author * {@link BroadcastReceiver} to enable oAuth2 token receiving. - */ + *-/ @Override public void onReceive(Context context, Intent intent) { @SuppressWarnings("unchecked") HashMap tokenResponse = (HashMap)intent.getExtras().get(OAuth2GetTokenService.TOKEN_RECEIVED_DATA); Log.d(TAG, "TokenReceiver->" + tokenResponse.get(OAuth2GetTokenService.TOKEN_ACCESS_TOKEN)); - dismissDialog(OAUTH2_LOGIN_PROGRESS); + dismissDialog(DIALOG_OAUTH2_LOGIN_PROGRESS); } } + */ - @Override - public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - if (operation instanceof ConnectionCheckOperation) { - - mStatusText = mStatusIcon = 0; - mStatusCorrect = false; - String t_url = ((TextView) findViewById(R.id.host_URL)).getText() - .toString().trim().toLowerCase(); - - switch (result.getCode()) { - case OK_SSL: - mIsSslConn = true; - mStatusIcon = android.R.drawable.ic_secure; - mStatusText = R.string.auth_secure_connection; - mStatusCorrect = true; - break; - - case OK_NO_SSL: - case OK: - mIsSslConn = false; - mStatusCorrect = true; - if (t_url.startsWith("http://") ) { - mStatusText = R.string.auth_connection_established; - mStatusIcon = R.drawable.ic_ok; - } else { - mStatusText = R.string.auth_nossl_plain_ok_title; - mStatusIcon = android.R.drawable.ic_partial_secure; - } - break; - - - case BAD_OC_VERSION: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_bad_oc_version_title; - break; - case WRONG_CONNECTION: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_wrong_connection_title; - break; - case TIMEOUT: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_timeout_title; - break; - case INCORRECT_ADDRESS: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_incorrect_address_title; - break; - - case SSL_RECOVERABLE_PEER_UNVERIFIED: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_ssl_unverified_server_title; - mLastSslUntrustedServerResult = result; - showDialog(DIALOG_SSL_VALIDATOR); - break; - - case SSL_ERROR: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_ssl_general_error_title; - break; - - case HOST_NOT_AVAILABLE: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_unknown_host_title; - break; - case NO_NETWORK_CONNECTION: - mStatusIcon = R.drawable.no_network; - mStatusText = R.string.auth_no_net_conn_title; - break; - case INSTANCE_NOT_CONFIGURED: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_not_configured_title; - break; - case FILE_NOT_FOUND: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_incorrect_path_title; - break; - case UNHANDLED_HTTP_CODE: - case UNKNOWN_ERROR: - mStatusIcon = R.drawable.common_error; - mStatusText = R.string.auth_unknown_error_title; - break; - default: - Log.e(TAG, "Incorrect connection checker result type: " + result.getHttpCode()); - } - setResultIconAndText(mStatusIcon, mStatusText); - if (!mStatusCorrect) - findViewById(R.id.refreshButton).setVisibility(View.VISIBLE); - else - findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE); - findViewById(R.id.buttonOK).setEnabled(mStatusCorrect); - - } else if (operation instanceof GetOAuth2AccessToken) { - - try { - dismissDialog(OAUTH2_LOGIN_PROGRESS); - } catch (IllegalArgumentException e) { - // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens - } - - if (result.isSuccess()) { - - /// time to test the retrieved access token on the ownCloud server - String url = ((TextView) findViewById(R.id.host_URL)).getText() - .toString().trim(); - if (url.endsWith("/")) - url = url.substring(0, url.length() - 1); - - Uri uri = null; - /*String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion); - - if (webdav_path == null) { - onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title)); - return; - }*/ - - String prefix = ""; - if (mIsSslConn) { - prefix = "https://"; - } else { - prefix = "http://"; - } - if (url.toLowerCase().startsWith("http://") - || url.toLowerCase().startsWith("https://")) { - prefix = ""; - } - - try { - mBaseUrl = prefix + url; - //String url_str = prefix + url + webdav_path; - String url_str = prefix + url + "/remote.php/odav"; - uri = Uri.parse(url_str); - - } catch (Exception e) { - // should never happen - onAuthenticationResult(false, getString(R.string.auth_incorrect_address_title)); - return; - } - - showDialog(DIALOG_LOGIN_PROGRESS); - String accessToken = ((GetOAuth2AccessToken)operation).getResultTokenMap().get(OAuth2Context.KEY_ACCESS_TOKEN); - Log.d(TAG, "Got ACCESS TOKEN: " + accessToken); - mAuthChkOperation = new ExistenceCheckOperation("", this, accessToken); - WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(uri, getApplicationContext()); - mAuthChkOperation.execute(client, this, mHandler); - - - } else { - TextView tv = (TextView) findViewById(R.id.oAuth_URL); - tv.setError("A valid authorization could not be obtained"); - - } - - } else if (operation instanceof ExistenceCheckOperation) { - - try { - dismissDialog(DIALOG_LOGIN_PROGRESS); - } catch (IllegalArgumentException e) { - // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens - } - - if (result.isSuccess()) { - TextView tv = (TextView) findViewById(R.id.oAuth_URL); - Log.d(TAG, "Checked access - time to save the account"); - - Uri uri = Uri.parse(mBaseUrl); - String username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong(); - String accountName = username + "@" + uri.getHost(); - if (uri.getPort() >= 0) { - accountName += ":" + uri.getPort(); - } - // TODO - check that accountName does not exist - Account account = new Account(accountName, AccountAuthenticator.ACCOUNT_TYPE); - AccountManager accManager = AccountManager.get(this); - accManager.addAccountExplicitly(account, "", null); // with our implementation, the password is never input in the app - - // Add this account as default in the preferences, if there is none - Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this); - if (defaultAccount == null) { - SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit(); - editor.putString("select_oc_account", accountName); - editor.commit(); - } - - /// account data to save by the AccountManager - final Intent intent = new Intent(); - intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountAuthenticator.ACCOUNT_TYPE); - intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name); - intent.putExtra(AccountManager.KEY_USERDATA, username); - - accManager.setAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, ((ExistenceCheckOperation) operation).getAccessToken()); - - accManager.setUserData(account, AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable.getDiscoveredVersion().toString()); - accManager.setUserData(account, AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl); - accManager.setUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE"); - - setAccountAuthenticatorResult(intent.getExtras()); - setResult(RESULT_OK, intent); - - /// enforce the first account synchronization - Bundle bundle = new Bundle(); - bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); - ContentResolver.requestSync(account, "org.owncloud", bundle); - - finish(); - - } else { - TextView tv = (TextView) findViewById(R.id.oAuth_URL); - tv.setError(result.getLogMessage()); - Log.d(TAG, "Access failed: " + result.getLogMessage()); - } - } - } - - + + /** + * Called from SslValidatorDialog when a new server certificate was correctly saved. + */ public void onSavedCertificate() { - mAuthThread = mConnChkRunnable.retry(this, mHandler); + mOperationThread = mOcServerChkOperation.retry(this, mHandler); } + /** + * Called from SslValidatorDialog when a new server certificate could not be saved + * when the user requested it. + */ @Override public void onFailedSavingCertificate() { showDialog(DIALOG_CERT_NOT_SAVED); diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index a717f0c6..1d75e692 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -217,7 +217,7 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements */ private void createFirstAccount() { Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT); - intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE }); + intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTHORITY }); startActivity(intent); // the new activity won't be created until this.onStart() and this.onResume() are finished; } @@ -346,12 +346,12 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements } private void startSynchronization() { - ContentResolver.cancelSync(null, AccountAuthenticator.AUTH_TOKEN_TYPE); // cancel the current synchronizations of any ownCloud account + ContentResolver.cancelSync(null, AccountAuthenticator.AUTHORITY); // cancel the current synchronizations of any ownCloud account Bundle bundle = new Bundle(); bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); ContentResolver.requestSync( AccountUtils.getCurrentOwnCloudAccount(this), - AccountAuthenticator.AUTH_TOKEN_TYPE, bundle); + AccountAuthenticator.AUTHORITY, bundle); } diff --git a/src/com/owncloud/android/ui/activity/LandingActivity.java b/src/com/owncloud/android/ui/activity/LandingActivity.java index 3af64853..e90d36a7 100644 --- a/src/com/owncloud/android/ui/activity/LandingActivity.java +++ b/src/com/owncloud/android/ui/activity/LandingActivity.java @@ -113,9 +113,9 @@ public class LandingActivity extends SherlockFragmentActivity implements dialog.dismiss(); switch (which) { case DialogInterface.BUTTON_POSITIVE: - Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); + Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT); intent.putExtra("authorities", - new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE }); + new String[] { AccountAuthenticator.AUTHORITY }); startActivity(intent); break; case DialogInterface.BUTTON_NEGATIVE: