ソースを参照

Adding support to OAuth2 'authorization code' grant type [WIP]

David A. Velasco 12 年 前
コミット
5be4bf17e1

+ 10 - 1
AndroidManifest.xml

@@ -120,7 +120,16 @@
         <activity
             android:name=".ui.activity.AuthenticatorActivity"
             android:exported="true"
-            android:theme="@style/Theme.ownCloud.noActionBar" >
+            android:theme="@style/Theme.ownCloud.noActionBar" 
+            android:launchMode="singleTask">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data android:scheme="oauth-mobile-app" />
+            </intent-filter>
         </activity>
 
         <service android:name=".files.services.FileDownloader" >

+ 2 - 2
src/com/owncloud/android/authenticator/AccountAuthenticator.java

@@ -204,8 +204,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);
+        //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);
     }

+ 31 - 6
src/com/owncloud/android/authenticator/oauth2/OAuth2Context.java

@@ -11,10 +11,35 @@ package com.owncloud.android.authenticator.oauth2;
 
 public class OAuth2Context {
     
-    public static final String OAUTH2_DEVICE_CLIENT_ID = "0000000000000.apps.googleusercontent.com";  
-    public static final String OAUTH2_DEVICE_CLIENT_SECRET = "XXXXXXXXXXXXXXXXXXXXXXXXX";
-    public static final String OAUTH_DEVICE_GETTOKEN_GRANT_TYPE = "http://oauth.net/grant_type/device/1.0";
-    public static final String OAUTH2_DEVICE_GETCODE_URL = "/o/oauth2/device/code";  
-    public static final String OAUTH2_DEVICE_GETTOKEN_URL = "/o/oauth2/token";
-    public static final String OAUTH2_DEVICE_GETCODE_SCOPES = "https://www.googleapis.com/auth/userinfo.email";
+    public static final String OAUTH2_G_DEVICE_CLIENT_ID = "1044165972576.apps.googleusercontent.com";  
+    public static final String OAUTH2_G_DEVICE_CLIENT_SECRET = "rwrA86fnIRCC3bZm0tWnKOkV";
+    public static final String OAUTH_G_DEVICE_GETTOKEN_GRANT_TYPE = "http://oauth.net/grant_type/device/1.0";
+    public static final String OAUTH2_G_DEVICE_GETCODE_URL = "https://accounts.google.com/o/oauth2/device/code";  
+    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_CLIENT_ID = "oc-android-test";
+    public static final String OAUTH2_F_SCOPE = "grades";
+    
+    public static final String OAUTH2_AUTH_CODE_GRANT_TYPE = "authorization_code";
+    public static final String OAUTH2_CODE_RESPONSE_TYPE = "code";
+
+    public static final String OAUTH2_TOKEN_RECEIVED_ERROR = "error";
+
+    public static final String MY_REDIRECT_URI = "oauth-mobile-app://callback";   // THIS CAN'T BE READ DYNAMICALLY; MUST BE DEFINED IN INSTALLATION TIME
+    
+    public static final String KEY_ACCESS_TOKEN = "access_token";
+    public static final String KEY_TOKEN_TYPE = "token_type";
+    public static final String KEY_EXPIRES_IN = "expires_in";
+    public static final String KEY_REFRESH_TOKEN = "refresh_token";
+    public static final String KEY_SCOPE = "scope";
+    public static final String KEY_ERROR = "error";
+    public static final String KEY_ERROR_DESCRIPTION = "error_description";
+    public static final String KEY_ERROR_URI = "error_uri";
+    public static final String KEY_REDIRECT_URI = "redirect_uri";
+    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";
 }

+ 43 - 9
src/com/owncloud/android/authenticator/oauth2/OAuth2GetCodeRunnable.java

@@ -11,7 +11,9 @@ 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;
 
@@ -28,17 +30,25 @@ 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_CLIENT_SCOPE  =  "scope";    
+    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;
@@ -54,9 +64,6 @@ public class OAuth2GetCodeRunnable implements Runnable {
 
     @Override
     public void run() {
-        ResultOAuthType mLatestResult;
-        String targetURI = null;
-        JSONObject codeResponseJson = null;
 
         if (!isOnline()) {
             postResult(ResultOAuthType.NO_NETWORK_CONNECTION,null);
@@ -69,14 +76,41 @@ public class OAuth2GetCodeRunnable implements Runnable {
             mUrl = "https://" + mUrl;
             mLatestResult = ResultOAuthType.OK_SSL;
         }
-        targetURI = mUrl + OAuth2Context.OAUTH2_DEVICE_GETCODE_URL;
 
-        ConnectorOAuth2 connectorOAuth2 = new ConnectorOAuth2(targetURI);
+        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<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
-            nameValuePairs.add(new BasicNameValuePair(CODE_CLIENT_ID, OAuth2Context.OAUTH2_DEVICE_CLIENT_ID));
-            nameValuePairs.add(new BasicNameValuePair(CODE_CLIENT_SCOPE,OAuth2Context.OAUTH2_DEVICE_GETCODE_SCOPES));
+            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) {
@@ -90,10 +124,10 @@ public class OAuth2GetCodeRunnable implements Runnable {
         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();

+ 9 - 10
src/com/owncloud/android/authenticator/oauth2/services/OAuth2GetTokenService.java

@@ -33,7 +33,7 @@ public class OAuth2GetTokenService extends Service {
 
     public static final String TOKEN_RECEIVED_MESSAGE = "TOKEN_RECEIVED";
     public static final String TOKEN_RECEIVED_DATA = "TOKEN_DATA";
-    public static final String TOKEN_BASE_URI = "baseURI";
+    public static final String TOKEN_URI = "TOKEN_URI";
     public static final String TOKEN_DEVICE_CODE = "device_code";
     public static final String TOKEN_INTERVAL = "interval";
     public static final String TOKEN_RECEIVED_ERROR = "error";
@@ -61,18 +61,17 @@ public class OAuth2GetTokenService extends Service {
         Bundle param = intent.getExtras();
 
         if (param != null) {
-            String mUrl = param.getString(TOKEN_BASE_URI);     
+            String mUrl = param.getString(TOKEN_URI);     
             if (!mUrl.startsWith("http://") || !mUrl.startsWith("https://")) {        
                 requestBaseURI = "https://" + mUrl;            
             }     
             requestDeviceCode = param.getString(TOKEN_DEVICE_CODE);
             requestInterval = param.getInt(TOKEN_INTERVAL);
             
-            Log.d(TAG, "onBind -> baseURI=" + requestBaseURI);
-            Log.d(TAG, "onBind -> requestDeviceCode=" + requestDeviceCode);
-            Log.d(TAG, "onBind -> requestInterval=" + requestInterval);                  
+            Log.d(TAG, "onStartCommand -> requestDeviceCode=" + requestDeviceCode);
+            Log.d(TAG, "onStartCommand -> requestInterval=" + requestInterval);                  
         } else  {
-            Log.e(TAG, "onBind -> params could not be null");
+            Log.e(TAG, "onStartCommand -> params could not be null");
         }
         startService();
         return Service.START_NOT_STICKY;
@@ -127,13 +126,13 @@ public class OAuth2GetTokenService extends Service {
         }        
 
         try{            
-            connectorOAuth2.setConnectorOAuth2Url(requestBaseURI + OAuth2Context.OAUTH2_DEVICE_GETTOKEN_URL);
+            connectorOAuth2.setConnectorOAuth2Url(requestBaseURI + OAuth2Context.OAUTH2_G_DEVICE_GETTOKEN_URL);
 
             List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
-            nameValuePairs.add(new BasicNameValuePair("client_id", OAuth2Context.OAUTH2_DEVICE_CLIENT_ID));
-            nameValuePairs.add(new BasicNameValuePair("client_secret", OAuth2Context.OAUTH2_DEVICE_CLIENT_SECRET));
+            nameValuePairs.add(new BasicNameValuePair("client_id", OAuth2Context.OAUTH2_G_DEVICE_CLIENT_ID));
+            nameValuePairs.add(new BasicNameValuePair("client_secret", OAuth2Context.OAUTH2_G_DEVICE_CLIENT_SECRET));
             nameValuePairs.add(new BasicNameValuePair("code",requestDeviceCode));            
-            nameValuePairs.add(new BasicNameValuePair("grant_type",OAuth2Context.OAUTH_DEVICE_GETTOKEN_GRANT_TYPE));  
+            nameValuePairs.add(new BasicNameValuePair("grant_type",OAuth2Context.OAUTH_G_DEVICE_GETTOKEN_GRANT_TYPE));  
 
             params = new UrlEncodedFormEntity(nameValuePairs);
         }

+ 15 - 6
src/com/owncloud/android/network/OwnCloudClientUtils.java

@@ -22,6 +22,7 @@ import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URL;
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
@@ -38,6 +39,7 @@ import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
 import org.apache.http.conn.ssl.X509HostnameVerifier;
 
 import com.owncloud.android.AccountUtils;
+import com.owncloud.android.authenticator.AccountAuthenticator;
 
 import eu.alefzero.webdav.WebdavClient;
 
@@ -75,16 +77,22 @@ public class OwnCloudClientUtils {
      * @return          A WebdavClient object ready to be used
      */
     public static WebdavClient createOwnCloudClient (Account account, Context context) {
-        Log.d(TAG, "Creating WebdavClient associated to " + account.name);
+        //Log.d(TAG, "Creating WebdavClient associated to " + account.name);
        
         Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(context, account));
         WebdavClient client = createOwnCloudClient(uri, context);
         
         String username = account.name.substring(0, account.name.lastIndexOf('@'));
-        String password = AccountManager.get(context).getPassword(account);
-        //String password = am.blockingGetAuthToken(mAccount, AccountAuthenticator.AUTH_TOKEN_TYPE, true);
+        /*if (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2)) {
+            // TODO - this is a trap; the OAuth access token shouldn't be saved as the account password
+            String accessToken = AccountManager.get(context).getPassword(account);
+            client.setCredentials("bearer", accessToken);
         
-        client.setCredentials(username, password);
+        } else {*/
+            String password = AccountManager.get(context).getPassword(account);
+            //String password = am.blockingGetAuthToken(mAccount, AccountAuthenticator.AUTH_TOKEN_TYPE, true);
+            client.setCredentials(username, password);
+        //}
         
         return client;
     }
@@ -100,7 +108,7 @@ public class OwnCloudClientUtils {
      * @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);
+        //Log.d(TAG, "Creating WebdavClient for " + username + "@" + uri);
         
         WebdavClient client = createOwnCloudClient(uri, context);
         
@@ -118,7 +126,7 @@ public class OwnCloudClientUtils {
      * @return          A WebdavClient object ready to be used
      */
     public static WebdavClient createOwnCloudClient(Uri uri, Context context) {
-        Log.d(TAG, "Creating WebdavClient for " + uri);
+        //Log.d(TAG, "Creating WebdavClient for " + uri);
         
         //allowSelfsignedCertificates(true);
         try {
@@ -270,4 +278,5 @@ public class OwnCloudClientUtils {
         return mConnManager;
     }
 
+
 }

+ 109 - 0
src/com/owncloud/android/operations/ExistenceCheckOperation.java

@@ -0,0 +1,109 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012 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
+ *   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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.operations;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.HeadMethod;
+
+import eu.alefzero.webdav.WebdavClient;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.util.Log;
+
+/**
+ * Operation to check the existence or absence of a path in a remote server.
+ * 
+ * @author David A. Velasco
+ */
+public class ExistenceCheckOperation extends RemoteOperation {
+    
+    /** Maximum time to wait for a response from the server in MILLISECONDs.  */
+    public static final int TIMEOUT = 10000;
+    
+    private static final String TAG = ExistenceCheckOperation.class.getSimpleName();
+    
+    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;
+    }
+
+    
+    /**
+     * Full constructor. Success of the operation will depend upon the value of successIfAbsent.
+     * 
+     * @param path              Path to append to the URL owned by the client instance.
+     * @param context           Android application context.
+     * @param successIfAbsent   When 'true', the operation finishes in success if the path does NOT exist in the remote server (HTTP 404).
+     */
+    public ExistenceCheckOperation(String path, Context context, boolean successIfAbsent) {
+        mPath = (path != null) ? path : "";
+        mContext = context;
+        mSuccessIfAbsent = successIfAbsent;
+    }
+    
+
+	@Override
+	protected RemoteOperationResult run(WebdavClient client) {
+        if (!isOnline()) {
+            return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
+        }
+        RemoteOperationResult result = null;
+        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);
+            result = new RemoteOperationResult(success, status);
+            Log.d(TAG, "Existence check for " + client.getBaseUri() + mPath + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + "finished with HTTP status " + status + (!success?"(FAIL)":""));
+            
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            Log.e(TAG, "Existence check for " + client.getBaseUri() + mPath + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + ": " + result.getLogMessage(), result.getException());
+            
+        } finally {
+            if (head != null)
+                head.releaseConnection();
+        }
+        return result;
+	}
+
+    private boolean isOnline() {
+        ConnectivityManager cm = (ConnectivityManager) mContext
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        return cm != null && cm.getActiveNetworkInfo() != null
+                && cm.getActiveNetworkInfo().isConnectedOrConnecting();
+    }
+
+}

+ 125 - 0
src/com/owncloud/android/operations/GetOAuth2AccessToken.java

@@ -0,0 +1,125 @@
+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<String, String> mOAuth2AuthorizationResponse;
+    private Map<String, String> mResultTokenMap;
+
+    
+    public GetOAuth2AccessToken(Map<String, String> oAuth2AuthorizationResponse) {
+        mOAuth2AuthorizationResponse = oAuth2AuthorizationResponse;
+        mResultTokenMap = null;
+    }
+    
+    
+    public Map<String, String> getOauth2AutorizationResponse() {
+        return mOAuth2AuthorizationResponse;
+    }
+
+    public Map<String, String> 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<String, String>();
+        
+        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));
+        }
+    }
+
+}

+ 4 - 3
src/com/owncloud/android/operations/RemoteOperationResult.java

@@ -45,8 +45,8 @@ import com.owncloud.android.network.CertificateCombinedException;
 public class RemoteOperationResult implements Serializable {
     
     /** Generated - to refresh every time the class changes */
-    private static final long serialVersionUID = -7805531062432602444L;
-
+    private static final long serialVersionUID = 5336333154035462033L;
+    
     
     public enum ResultCode { 
         OK,
@@ -69,7 +69,8 @@ public class RemoteOperationResult implements Serializable {
         CANCELLED, 
         INVALID_LOCAL_FILE_NAME, 
         INVALID_OVERWRITE,
-        CONFLICT
+        CONFLICT, 
+        OAUTH2_ERROR
     }
 
     private boolean mSuccess = false;

+ 258 - 52
src/com/owncloud/android/ui/activity/AuthenticatorActivity.java

@@ -21,6 +21,7 @@ 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;
@@ -30,14 +31,18 @@ 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.ExistenceCheckOperation;
+import com.owncloud.android.operations.GetOAuth2AccessToken;
 import com.owncloud.android.operations.OnRemoteOperationListener;
 import com.owncloud.android.operations.RemoteOperation;
 import com.owncloud.android.operations.RemoteOperationResult;
@@ -91,15 +96,17 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
     private Thread mAuthThread;
     private AuthenticationRunnable mAuthRunnable;
-    //private ConnectionCheckerRunnable mConnChkRunnable = null;
     private ConnectionCheckOperation mConnChkRunnable;
+    private ExistenceCheckOperation mAuthChkOperation;
     private final Handler mHandler = new Handler();
     private String mBaseUrl;
+    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 RemoteOperationResult mLastSslUntrustedServerResult;
@@ -112,11 +119,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     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_BASE_URL = "BASE_URL"; 
     private static final String OAUTH2_IS_CHECKED = "OAUTH2_IS_CHECKED";    
     private Thread mOAuth2GetCodeThread;
     private OAuth2GetCodeRunnable mOAuth2GetCodeRunnable;     
-    private String oAuth2BaseUrl;
     private TokenReceiver tokenReceiver;
     private JSONObject codeResponseJson; 
     private int mOAuth2StatusText, mOAuth2StatusIcon;    
@@ -125,9 +130,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     
     // 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 OAUTH2_URL_TEXT = "OAUTH2_URL_TEXT";
     private static final String ACCOUNT_USERNAME = "ACCOUNT_USERNAME";
     private static final String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD";
+    
+    //private boolean mNewRedirectUriCaptured;
+    private Uri mNewCapturedUriFromOAuth2Redirection;
 
     // END of oAuth2 variables.
     
@@ -140,8 +147,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         ImageView iv2 = (ImageView) findViewById(R.id.viewPassword);
         TextView tv = (TextView) findViewById(R.id.host_URL);
         TextView tv2 = (TextView) findViewById(R.id.account_password);
-        // New textview to oAuth2 URL.
-        TextView tv3 = (TextView) findViewById(R.id.oAuth_URL);
+        EditText oauth2Url = (EditText)findViewById(R.id.oAuth_URL);
+        oauth2Url.setText("OWNCLOUD AUTHORIZATION PROVIDER IN TEST");
 
         if (savedInstanceState != null) {
             mStatusIcon = savedInstanceState.getInt(STATUS_ICON);
@@ -153,14 +160,17 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             if (!mStatusCorrect)
                 iv.setVisibility(View.VISIBLE);
             else
-                iv.setVisibility(View.INVISIBLE);            
+                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));
-            oAuth2BaseUrl = savedInstanceState.getString(OAUTH2_BASE_URL);
                 // 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 {
@@ -175,8 +185,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             // Getting contents of each field.
             EditText hostUrl = (EditText)findViewById(R.id.host_URL);
             hostUrl.setText(savedInstanceState.getString(HOST_URL_TEXT), TextView.BufferType.EDITABLE);
-            EditText oauth2Url = (EditText)findViewById(R.id.oAuth_URL);
-            oauth2Url.setText(savedInstanceState.getString(OAUTH2_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);
@@ -192,17 +200,32 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         iv2.setOnClickListener(this);
         tv.setOnFocusChangeListener(this);
         tv2.setOnFocusChangeListener(this);
-        // Setting the listener for oAuth2 URL TextView.
-        tv3.setOnFocusChangeListener(this);
         
-        super.onCreate(savedInstanceState);
+        mNewCapturedUriFromOAuth2Redirection = null;
+        
+        Log.d(TAG, "onCreate");
     }
 
+    
+    @Override
+    protected void onNewIntent (Intent intent) {
+        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);
@@ -212,12 +235,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         if (codeResponseJson != null){
             outState.putString(OAUTH2_CODE_RESULT, codeResponseJson.toString());
         }
-        outState.putString(OAUTH2_BASE_URL, oAuth2BaseUrl);
         // 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(OAUTH2_URL_TEXT,((TextView) findViewById(R.id.oAuth_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());
         
@@ -252,9 +273,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         case OAUTH2_LOGIN_PROGRESS: {
             ProgressDialog working_dialog = new ProgressDialog(this);
             try {
-                working_dialog.setMessage(String.format(getString(R.string.oauth_code_validation_message), 
-                        codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL), 
-                        codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE)));
+                if (codeResponseJson != null && codeResponseJson.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"));
+                }
             } catch (JSONException e) {
                 Log.e(TAG, "onCreateDialog->JSONException: " + e.toString());
             }
@@ -307,6 +332,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         switch (id) {
         case DIALOG_LOGIN_PROGRESS:
         case DIALOG_CERT_NOT_SAVED:
+        case OAUTH2_LOGIN_PROGRESS:
             break;
         case DIALOG_SSL_VALIDATOR: {
             ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
@@ -320,12 +346,19 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     @Override
     protected void onResume() {
         Log.d(TAG, "onResume() start");
-        // Registering token receiver. We must listening to the service that is pooling to the oAuth server for a token.
+        // (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();
     }
 
@@ -382,8 +415,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             accManager.setUserData(account, AccountAuthenticator.KEY_OC_URL,
                     url.toString());
             accManager.setUserData(account,
-                    AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable
-                            .getDiscoveredVersion().toString());
+                    AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString());
             
             accManager.setUserData(account,
                     AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);
@@ -432,9 +464,25 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 || url.toLowerCase().startsWith("https://")) {
             prefix = "";
         }
-        continueConnection(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();
+    }
+
     public void onRegisterClick(View view) {
         Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse("https://owncloud.com/mobile/new"));
         setResult(RESULT_CANCELED);
@@ -452,8 +500,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             url = url.substring(0, url.length() - 1);
 
         URL uri = null;
-        String webdav_path = AccountUtils.getWebdavPath(mConnChkRunnable
-                .getDiscoveredVersion());
+        mDiscoveredVersion = mConnChkRunnable.getDiscoveredVersion();
+        String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion);
         
         if (webdav_path == null) {
             onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title));
@@ -567,11 +615,12 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                     setResultIconAndText(R.drawable.progress_small,
                             R.string.auth_testing_connection);
                     //mConnChkRunnable = new ConnectionCheckerRunnable(uri, this);
-                    mConnChkRunnable = new ConnectionCheckOperation(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(
@@ -593,28 +642,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 v.setInputType(input_type);
                 iv.setVisibility(View.INVISIBLE);
             }
-        // If the focusChange occurs on the oAuth2 URL field, we do this.
-        } else if (view.getId() == R.id.oAuth_URL) {
-            if (!hasFocus) {
-                TextView tv3 = ((TextView) findViewById(R.id.oAuth_URL));
-                // We get the URL of oAuth2 server.
-                oAuth2BaseUrl = tv3.getText().toString().trim();
-                if (oAuth2BaseUrl.length() != 0) {
-                    // We start a thread to get user_code from the oAuth2 server.
-                    setOAuth2ResultIconAndText(R.drawable.progress_small, R.string.oauth_login_connection);
-                    mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(oAuth2BaseUrl, this);
-                    mOAuth2GetCodeRunnable.setListener(this, mHandler);
-                    mOAuth2GetCodeThread = new Thread(mOAuth2GetCodeRunnable);
-                    mOAuth2GetCodeThread.start();
-                } else {
-                    findViewById(R.id.refreshButton).setVisibility(
-                            View.INVISIBLE);
-                    setOAuth2ResultIconAndText(0, 0);
-                }
-            } else {
-                // avoids that the 'connect' button can be clicked if the test was previously passed
-                findViewById(R.id.buttonOK).setEnabled(false); 
-            }
         }
     }
 
@@ -715,15 +742,18 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     public void onOAuth2GetCodeResult(ResultOAuthType type, JSONObject responseJson) {
         if ((type == ResultOAuthType.OK_SSL)||(type == ResultOAuthType.OK_NO_SSL)) {
             codeResponseJson = responseJson;
-            startOAuth2Authentication();
+            if (codeResponseJson != 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);
         }
     }
 
     // If the results of getting the user_code and verification_url are OK, we get the received data and we start
-    // the pooling service to oAuth2 server to get a valid token.
-    private void startOAuth2Authentication () {
+    // the polling service to oAuth2 server to get a valid token.
+    private void getOAuth2AccessTokenFromJsonResponse() {
         String deviceCode = null;
         String verificationUrl = null;
         String userCode = null;
@@ -762,7 +792,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         // Starting the pooling service.
         try {
             Intent tokenService = new Intent(this, OAuth2GetTokenService.class);
-            tokenService.putExtra(OAuth2GetTokenService.TOKEN_BASE_URI, oAuth2BaseUrl);
+            tokenService.putExtra(OAuth2GetTokenService.TOKEN_URI, OAuth2Context.OAUTH2_G_DEVICE_GETTOKEN_URL);
             tokenService.putExtra(OAuth2GetTokenService.TOKEN_DEVICE_CODE, deviceCode);
             tokenService.putExtra(OAuth2GetTokenService.TOKEN_INTERVAL, interval);
 
@@ -771,7 +801,61 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         catch (Exception e) {
             Log.e(TAG, "tokenService creation problem :", e);
         }
+        
     }   
+    
+    private void getOAuth2AccessTokenFromCapturedRedirection() {
+        Map<String, String> responseValues = new HashMap<String, String>();
+        //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);
+    }
+
+    
 
     // We get data from the oAuth2 token service with this broadcast receiver.
     private class TokenReceiver extends BroadcastReceiver {
@@ -792,7 +876,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
 	@Override
 	public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
-		if (operation.equals(mConnChkRunnable)) {
+		if (operation instanceof ConnectionCheckOperation) {
 		    
 	        mStatusText = mStatusIcon = 0;
 	        mStatusCorrect = false;
@@ -880,6 +964,128 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 	        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, "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);
+                tv.setError("OOOOOKKKKKK");
+		        Log.d(TAG, "OOOOK!!!!");
+		        /**
+		        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);
+		        /// TODO SAVE THE ACCESS TOKEN, HERE OR IN SOME BETTER PLACE
+		        //accManager.addAccountExplicitly(account, mAccesToken, null);  //// IS THIS REALLY NEEDED? IS NOT REDUNDANT WITH SETACCOUNTAUTHENTICATORRESULT?
+
+		        // 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_AUTHTOKEN, AccountAuthenticator.ACCOUNT_TYPE);
+		        intent.putExtra(AccountManager.KEY_USERDATA, username);
+		        intent.putExtra(AccountManager.KEY_AUTHTOKEN, mAccessToken)
+
+		        accManager.setUserData(account, AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable.getDiscoveredVersion().toString());
+		        accManager.setUserData(account, AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);
+
+		        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, "NOOOOO " + result.getLogMessage());
+		    }
 		}
 	}
 

+ 1 - 1
src/eu/alefzero/webdav/WebdavClient.java

@@ -68,7 +68,7 @@ public class WebdavClient extends HttpClient {
         getState().setCredentials(AuthScope.ANY,
                 getCredentials(username, password));
     }
-
+    
     private Credentials getCredentials(String username, String password) {
         if (mCredentials == null)
             mCredentials = new UsernamePasswordCredentials(username, password);