From 7b88fb4e6cca8efcb82e7f98ce49085583583888 Mon Sep 17 00:00:00 2001
From: "David A. Velasco" <dvelasco@owncloud.com>
Date: Thu, 15 Dec 2016 11:12:19 +0100
Subject: [PATCH 1/3] Adapt authentication with basic credentials to reuse
 server sessions for server >= 9.1

---
 .../lib/common/OwnCloudBasicCredentials.java  |  62 +--
 .../lib/common/OwnCloudClientFactory.java     |  96 ++--
 .../common/OwnCloudCredentialsFactory.java    |  90 ++--
 .../lib/common/accounts/AccountUtils.java     | 430 ++++++++++--------
 .../lib/resources/status/OwnCloudVersion.java |   6 +
 5 files changed, 374 insertions(+), 310 deletions(-)

diff --git a/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java
index a64aa525..bf7c62c1 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java
+++ b/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java
@@ -32,41 +32,49 @@ import org.apache.commons.httpclient.auth.AuthScope;
 
 public class OwnCloudBasicCredentials implements OwnCloudCredentials {
 
-	private String mUsername;
-	private String mPassword;
+    private String mUsername;
+    private String mPassword;
+    private boolean mAuthenticationPreemptive;
 
-	public OwnCloudBasicCredentials(String username, String password) {
-		mUsername = username != null ? username : "";
-		mPassword = password != null ? password : "";
-	}
+    public OwnCloudBasicCredentials(String username, String password) {
+        mUsername = username != null ? username : "";
+        mPassword = password != null ? password : "";
+        mAuthenticationPreemptive = true;
+    }
 
-	@Override
-	public void applyTo(OwnCloudClient client) {
+    public OwnCloudBasicCredentials(String username, String password, boolean sessionEnabled) {
+        mUsername = username != null ? username : "";
+        mPassword = password != null ? password : "";
+        mAuthenticationPreemptive = !sessionEnabled;
+    }
+
+    @Override
+    public void applyTo(OwnCloudClient client) {
         List<String> authPrefs = new ArrayList<String>(1);
         authPrefs.add(AuthPolicy.BASIC);
-        client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);        
-        
-        client.getParams().setAuthenticationPreemptive(true);
+        client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
+
+        client.getParams().setAuthenticationPreemptive(mAuthenticationPreemptive);
         client.getParams().setCredentialCharset(OwnCloudCredentialsFactory.CREDENTIAL_CHARSET);
         client.getState().setCredentials(
-        		AuthScope.ANY, 
-        		new UsernamePasswordCredentials(mUsername, mPassword)
-		);
-	}
+            AuthScope.ANY,
+            new UsernamePasswordCredentials(mUsername, mPassword)
+        );
+    }
 
-	@Override
-	public String getUsername() {
-		return mUsername;
-	}
+    @Override
+    public String getUsername() {
+        return mUsername;
+    }
 
-	@Override
-	public String getAuthToken() {
-		return mPassword;
-	}
+    @Override
+    public String getAuthToken() {
+        return mPassword;
+    }
 
-	@Override
-	public boolean authTokenExpires() {
-		return false;
-	}
+    @Override
+    public boolean authTokenExpires() {
+        return false;
+    }
 
 }
diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java b/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java
index 27285353..b267e1b3 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java
+++ b/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java
@@ -42,6 +42,7 @@ import com.owncloud.android.lib.common.accounts.AccountUtils;
 import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
 import com.owncloud.android.lib.common.network.NetworkUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 
 public class OwnCloudClientFactory {
     
@@ -69,7 +70,11 @@ public class OwnCloudClientFactory {
      * @throws IOException                  If there was some I/O error while getting the
      *                                      authorization token for the account.
      * @throws AccountNotFoundException     If 'account' is unknown for the AccountManager
+     *
+     * @deprecated :    Will be deleted in version 1.0.
+     *                  Use {@link #createOwnCloudClient(Account, Context, Activity)} instead.
      */
+    @Deprecated
     public static OwnCloudClient createOwnCloudClient (Account account, Context appContext)
             throws OperationCanceledException, AuthenticatorException, IOException,
             AccountNotFoundException {
@@ -86,35 +91,40 @@ public class OwnCloudClientFactory {
         String username = AccountUtils.getUsernameForAccount(account);
         if (isOauth2) {
             String accessToken = am.blockingGetAuthToken(
-            		account, 
-            		AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), 
-            		false);
+                account,
+                AccountTypeUtils.getAuthTokenTypeAccessToken(account.type),
+                false);
             
             client.setCredentials(
-            		OwnCloudCredentialsFactory.newBearerCredentials(accessToken)
+                OwnCloudCredentialsFactory.newBearerCredentials(accessToken)
     		);
         
         } else if (isSamlSso) {    // TODO avoid a call to getUserData here
             String accessToken = am.blockingGetAuthToken(
-            		account, 
-            		AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), 
-            		false);
+                account,
+                AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type),
+                false);
             
             client.setCredentials(
-            		OwnCloudCredentialsFactory.newSamlSsoCredentials(username, accessToken)
-    		);
+                OwnCloudCredentialsFactory.newSamlSsoCredentials(username, accessToken)
+            );
 
         } else {
             //String password = am.getPassword(account);
             String password = am.blockingGetAuthToken(
-            		account, 
-            		AccountTypeUtils.getAuthTokenTypePass(account.type), 
-            		false);
-            
+                account,
+                AccountTypeUtils.getAuthTokenTypePass(account.type),
+                false);
+
+            OwnCloudVersion version = AccountUtils.getServerVersionForAccount(account, appContext);
             client.setCredentials(
-            		OwnCloudCredentialsFactory.newBasicCredentials(username, password)
-    		);
-            
+                OwnCloudCredentialsFactory.newBasicCredentials(
+                    username,
+                    password,
+                    (version != null && version.isSessionMonitoringSupported())
+                )
+            );
+
         }
         
         // Restore cookies
@@ -140,35 +150,35 @@ public class OwnCloudClientFactory {
         String username = AccountUtils.getUsernameForAccount(account);
         if (isOauth2) {    // TODO avoid a call to getUserData here
             AccountManagerFuture<Bundle> future =  am.getAuthToken(
-            		account,  
-            		AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), 
-            		null, 
-            		currentActivity, 
-            		null, 
-            		null);
+                account,
+                AccountTypeUtils.getAuthTokenTypeAccessToken(account.type),
+                null,
+                currentActivity,
+                null,
+                null);
             
             Bundle result = future.getResult();
             String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
             if (accessToken == null) throw new AuthenticatorException("WTF!");
             client.setCredentials(
-            		OwnCloudCredentialsFactory.newBearerCredentials(accessToken)
-    		);
+                OwnCloudCredentialsFactory.newBearerCredentials(accessToken)
+            );
 
         } else if (isSamlSso) {    // TODO avoid a call to getUserData here
             AccountManagerFuture<Bundle> future =  am.getAuthToken(
-            		account, 
-            		AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), 
-            		null, 
-            		currentActivity, 
-            		null, 
-            		null);
+               account,
+               AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type),
+                null,
+                currentActivity,
+                null,
+                null);
             
             Bundle result = future.getResult();
             String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
             if (accessToken == null) throw new AuthenticatorException("WTF!");
             client.setCredentials(
-            		OwnCloudCredentialsFactory.newSamlSsoCredentials(username, accessToken)
-    		);
+                OwnCloudCredentialsFactory.newSamlSsoCredentials(username, accessToken)
+            );
 
 
         } else {
@@ -176,18 +186,24 @@ public class OwnCloudClientFactory {
             //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(),
             // false);
             AccountManagerFuture<Bundle> future =  am.getAuthToken(
-            		account,  
-            		AccountTypeUtils.getAuthTokenTypePass(account.type), 
-            		null, 
-            		currentActivity, 
-            		null, 
-            		null);
+                account,
+                AccountTypeUtils.getAuthTokenTypePass(account.type),
+                null,
+                currentActivity,
+                null,
+                null
+            );
             
             Bundle result = future.getResult();
             String password = result.getString(AccountManager.KEY_AUTHTOKEN);
+            OwnCloudVersion version = AccountUtils.getServerVersionForAccount(account, appContext);
             client.setCredentials(
-            		OwnCloudCredentialsFactory.newBasicCredentials(username, password)
-    		);
+                OwnCloudCredentialsFactory.newBasicCredentials(
+                    username,
+                    password,
+                    (version != null && version.isSessionMonitoringSupported())
+                )
+            );
         }
         
         // Restore cookies
diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java b/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java
index c9ea3421..647dac8f 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java
+++ b/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java
@@ -26,55 +26,61 @@ package com.owncloud.android.lib.common;
 
 public class OwnCloudCredentialsFactory {
 
-	public static final String CREDENTIAL_CHARSET = "UTF-8";
+    public static final String CREDENTIAL_CHARSET = "UTF-8";
 
-	private static OwnCloudAnonymousCredentials sAnonymousCredentials;
+    private static OwnCloudAnonymousCredentials sAnonymousCredentials;
 
-	public static OwnCloudCredentials newBasicCredentials(String username, String password) {
-		return new OwnCloudBasicCredentials(username, password);
-	}
-	
-	public static OwnCloudCredentials newBearerCredentials(String authToken) {
+    public static OwnCloudCredentials newBasicCredentials(String username, String password) {
+        return new OwnCloudBasicCredentials(username, password);
+    }
+
+    public static OwnCloudCredentials newBasicCredentials(
+        String username, String password, boolean sessionEnabled
+    ) {
+        return new OwnCloudBasicCredentials(username, password, sessionEnabled);
+    }
+
+    public static OwnCloudCredentials newBearerCredentials(String authToken) {
         return new OwnCloudBearerCredentials(authToken);
-	}
-    
-	public static OwnCloudCredentials newSamlSsoCredentials(String username, String sessionCookie) {
-		return new OwnCloudSamlSsoCredentials(username, sessionCookie);
-	}
+    }
 
-	public static final OwnCloudCredentials getAnonymousCredentials() {
-		if (sAnonymousCredentials == null) {
-			sAnonymousCredentials = new OwnCloudAnonymousCredentials();
-		}
-		return sAnonymousCredentials;
-	}
+    public static OwnCloudCredentials newSamlSsoCredentials(String username, String sessionCookie) {
+        return new OwnCloudSamlSsoCredentials(username, sessionCookie);
+    }
 
-	public static final class OwnCloudAnonymousCredentials implements OwnCloudCredentials {
-		
-		protected OwnCloudAnonymousCredentials() {
-		}
-		
-		@Override
-		public void applyTo(OwnCloudClient client) {
-			client.getState().clearCredentials();
-			client.getState().clearCookies();
-		}
+    public static final OwnCloudCredentials getAnonymousCredentials() {
+        if (sAnonymousCredentials == null) {
+            sAnonymousCredentials = new OwnCloudAnonymousCredentials();
+        }
+        return sAnonymousCredentials;
+    }
 
-		@Override
-		public String getAuthToken() {
-			return "";
-		}
+    public static final class OwnCloudAnonymousCredentials implements OwnCloudCredentials {
 
-		@Override
-		public boolean authTokenExpires() {
-			return false;
-		}
+        protected OwnCloudAnonymousCredentials() {
+        }
 
-		@Override
-		public String getUsername() {
-			// no user name
-			return null;
-		}
-	}
+        @Override
+        public void applyTo(OwnCloudClient client) {
+            client.getState().clearCredentials();
+            client.getState().clearCookies();
+        }
+
+        @Override
+        public String getAuthToken() {
+            return "";
+        }
+
+        @Override
+        public boolean authTokenExpires() {
+            return false;
+        }
+
+        @Override
+        public String getUsername() {
+            // no user name
+            return null;
+        }
+    }
 
 }
diff --git a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java
index 33f02137..3359593c 100644
--- a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java
+++ b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java
@@ -44,9 +44,9 @@ import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 
 public class AccountUtils {
-	
-	private static final String TAG = AccountUtils.class.getSimpleName();
-	
+
+    private static final String TAG = AccountUtils.class.getSimpleName();
+
     public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php";
     public static final String WEBDAV_PATH_2_0 = "/files/webdav.php";
     public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav";
@@ -59,11 +59,11 @@ public class AccountUtils {
     /**
      * Returns the proper URL path to access the WebDAV interface of an ownCloud server,
      * according to its version and the authorization method used.
-     * 
-     * @param	version         	Version of ownCloud server.
-     * @param 	supportsOAuth		If true, access with OAuth 2 authorization is considered. 
-     * @param 	supportsSamlSso		If true, and supportsOAuth is false, access with SAML-based single-sign-on is considered.
-     * @return 						WebDAV path for given OC version, null if OC version unknown
+     *
+     * @param supportsOAuth   If true, access with OAuth 2 authorization is considered.
+     * @param supportsSamlSso If true, and supportsOAuth is false, access with SAML-based single-sign-on is considered.
+     * @return WebDAV path for given OC version, null if OC version unknown
+     * @param    version Version of ownCloud server.
      */
     public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth, boolean supportsSamlSso) {
         if (version != null) {
@@ -76,73 +76,72 @@ public class AccountUtils {
             if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)
                 return WEBDAV_PATH_4_0;
             if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0
-                    || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)
+                || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)
                 return WEBDAV_PATH_2_0;
             if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)
                 return WEBDAV_PATH_1_2;
         }
         return null;
     }
-    
+
     /**
      * Constructs full url to host and webdav resource basing on host version
-     * 
-     * @deprecated 		To be removed in release 1.0. 
-     * 
+     *
      * @param context
      * @param account
      * @return url or null on failure
-     * @throws AccountNotFoundException     When 'account' is unknown for the AccountManager
+     * @throws AccountNotFoundException When 'account' is unknown for the AccountManager
+     * @deprecated To be removed in release 1.0.
      */
     @Deprecated
     public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException {
         AccountManager ama = AccountManager.get(context);
         String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL);
-        String version  = ama.getUserData(account, Constants.KEY_OC_VERSION);
+        String version = ama.getUserData(account, Constants.KEY_OC_VERSION);
         boolean supportsOAuth = (ama.getUserData(account, Constants.KEY_SUPPORTS_OAUTH2) != null);
         boolean supportsSamlSso = (ama.getUserData(account, Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null);
         OwnCloudVersion ver = new OwnCloudVersion(version);
         String webdavpath = getWebdavPath(ver, supportsOAuth, supportsSamlSso);
 
-        if (baseurl == null || webdavpath == null) 
+        if (baseurl == null || webdavpath == null)
             throw new AccountNotFoundException(account, "Account not found", null);
-        
+
         return baseurl + webdavpath;
     }
-    
-    /**
-     * Extracts url server from the account
-     * 
-     * @deprecated 	This method will be removed in version 1.0.
-     *  			Use {@link #getBaseUrlForAccount(Context, Account)}
-     *  		 	instead.   
-     * 
-     * @param context
-     * @param account
-     * @return url server or null on failure
-     * @throws AccountNotFoundException     When 'account' is unknown for the AccountManager
-     */
-    @Deprecated
-    public static String constructBasicURLForAccount(Context context, Account account) 
-    		throws AccountNotFoundException {
-    	return getBaseUrlForAccount(context, account);
-    }
 
     /**
      * Extracts url server from the account
+     *
      * @param context
      * @param account
      * @return url server or null on failure
-     * @throws AccountNotFoundException     When 'account' is unknown for the AccountManager
+     * @throws AccountNotFoundException When 'account' is unknown for the AccountManager
+     * @deprecated This method will be removed in version 1.0.
+     * Use {@link #getBaseUrlForAccount(Context, Account)}
+     * instead.
      */
-    public static String getBaseUrlForAccount(Context context, Account account) 
-    		throws AccountNotFoundException {
+    @Deprecated
+    public static String constructBasicURLForAccount(Context context, Account account)
+        throws AccountNotFoundException {
+        return getBaseUrlForAccount(context, account);
+    }
+
+    /**
+     * Extracts url server from the account
+     *
+     * @param context
+     * @param account
+     * @return url server or null on failure
+     * @throws AccountNotFoundException When 'account' is unknown for the AccountManager
+     */
+    public static String getBaseUrlForAccount(Context context, Account account)
+        throws AccountNotFoundException {
         AccountManager ama = AccountManager.get(context.getApplicationContext());
         String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL);
-        
-        if (baseurl == null ) 
+
+        if (baseurl == null)
             throw new AccountNotFoundException(account, "Account not found", null);
-        
+
         return baseurl;
     }
 
@@ -150,8 +149,8 @@ public class AccountUtils {
     /**
      * Get the username corresponding to an OC account.
      *
-     * @param account   An OC account
-     * @return          Username for the given account, extracted from the account.name
+     * @param account An OC account
+     * @return Username for the given account, extracted from the account.name
      */
     public static String getUsernameForAccount(Account account) {
         String username = null;
@@ -164,62 +163,86 @@ public class AccountUtils {
     }
 
     /**
-     * 
-     * @return
-     * @throws IOException 
-     * @throws AuthenticatorException 
-     * @throws OperationCanceledException 
+     * Get the stored server version corresponding to an OC account.
+     *
+     * @param account   An OC account
+     * @param context   Application context
+     * @return Version of the OC server, according to last check
      */
-	public static OwnCloudCredentials getCredentialsForAccount(Context context, Account account) 
-			throws OperationCanceledException, AuthenticatorException, IOException {
-		
-		OwnCloudCredentials credentials = null;
+    public static OwnCloudVersion getServerVersionForAccount(Account account, Context context) {
+        AccountManager ama = AccountManager.get(context);
+        OwnCloudVersion version = null;
+        try {
+            String versionString = ama.getUserData(account, Constants.KEY_OC_VERSION);
+            version = new OwnCloudVersion(versionString);
+
+        } catch (Exception e) {
+            Log_OC.e(TAG, "Couldn't get a the server version for an account", e);
+        }
+        return version;
+    }
+
+    /**
+     * @return
+     * @throws IOException
+     * @throws AuthenticatorException
+     * @throws OperationCanceledException
+     */
+    public static OwnCloudCredentials getCredentialsForAccount(Context context, Account account)
+        throws OperationCanceledException, AuthenticatorException, IOException {
+
+        OwnCloudCredentials credentials = null;
         AccountManager am = AccountManager.get(context);
-        
+
         boolean isOauth2 = am.getUserData(
-        		account, 
-        		AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null;
-        
+            account,
+            AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null;
+
         boolean isSamlSso = am.getUserData(
-        		account, 
-        		AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null;
+            account,
+            AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null;
 
         String username = AccountUtils.getUsernameForAccount(account);
+        OwnCloudVersion version = new OwnCloudVersion(am.getUserData(account, Constants.KEY_OC_VERSION));
 
-        if (isOauth2) {    
+        if (isOauth2) {
             String accessToken = am.blockingGetAuthToken(
-            		account, 
-            		AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), 
-            		false);
-            
+                account,
+                AccountTypeUtils.getAuthTokenTypeAccessToken(account.type),
+                false);
+
             credentials = OwnCloudCredentialsFactory.newBearerCredentials(accessToken);
-        
+
         } else if (isSamlSso) {
             String accessToken = am.blockingGetAuthToken(
-            		account, 
-            		AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), 
-            		false);
-            
+                account,
+                AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type),
+                false);
+
             credentials = OwnCloudCredentialsFactory.newSamlSsoCredentials(username, accessToken);
 
         } else {
             String password = am.blockingGetAuthToken(
-            		account, 
-            		AccountTypeUtils.getAuthTokenTypePass(account.type), 
-            		false);
-            
-            credentials = OwnCloudCredentialsFactory.newBasicCredentials(username, password);
-        }
-        
-        return credentials;
-        
-	}
+                account,
+                AccountTypeUtils.getAuthTokenTypePass(account.type),
+                false);
+
+            credentials = OwnCloudCredentialsFactory.newBasicCredentials(
+                username,
+                password,
+                version.isSessionMonitoringSupported()
+            );
+        }
+
+        return credentials;
+
+    }
+
 
-	
     public static String buildAccountNameOld(Uri serverBaseUrl, String username) {
-    	if (serverBaseUrl.getScheme() == null) {
-    		serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString()); 
-    	}
+        if (serverBaseUrl.getScheme() == null) {
+            serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString());
+        }
         String accountName = username + "@" + serverBaseUrl.getHost();
         if (serverBaseUrl.getPort() >= 0) {
             accountName += ":" + serverBaseUrl.getPort();
@@ -228,9 +251,9 @@ public class AccountUtils {
     }
 
     public static String buildAccountName(Uri serverBaseUrl, String username) {
-    	if (serverBaseUrl.getScheme() == null) {
-    		serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString());
-    	}
+        if (serverBaseUrl.getScheme() == null) {
+            serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString());
+        }
 
         // Remove http:// or https://
         String url = serverBaseUrl.toString();
@@ -242,148 +265,153 @@ public class AccountUtils {
         return accountName;
     }
 
-	public static void saveClient(OwnCloudClient client, Account savedAccount, Context context) {
+    public static void saveClient(OwnCloudClient client, Account savedAccount, Context context) {
 
-		// Account Manager
-		AccountManager ac = AccountManager.get(context.getApplicationContext());
+        // Account Manager
+        AccountManager ac = AccountManager.get(context.getApplicationContext());
 
-		if (client != null) {
-			String cookiesString = client.getCookiesString();
-			if (!"".equals(cookiesString)) {
-				ac.setUserData(savedAccount, Constants.KEY_COOKIES, cookiesString); 
-				// Log_OC.d(TAG, "Saving Cookies: "+ cookiesString );
-			}
-		}
+        if (client != null) {
+            String cookiesString = client.getCookiesString();
+            if (!"".equals(cookiesString)) {
+                ac.setUserData(savedAccount, Constants.KEY_COOKIES, cookiesString);
+                // Log_OC.d(TAG, "Saving Cookies: "+ cookiesString );
+            }
+        }
 
-	}
-	
-	
-  /**
-  * Restore the client cookies
-  * @param account
-  * @param client 
-  * @param context
-  */
-	public static void restoreCookies(Account account, OwnCloudClient client, Context context) {
+    }
 
-		Log_OC.d(TAG, "Restoring cookies for " + account.name);
 
-		// Account Manager
-		AccountManager am = AccountManager.get(context.getApplicationContext());
+    /**
+     * Restore the client cookies
+     *
+     * @param account
+     * @param client
+     * @param context
+     */
+    public static void restoreCookies(Account account, OwnCloudClient client, Context context) {
 
-		Uri serverUri = (client.getBaseUri() != null)? client.getBaseUri() : client.getWebdavUri();
+        Log_OC.d(TAG, "Restoring cookies for " + account.name);
 
-		String cookiesString = am.getUserData(account, Constants.KEY_COOKIES);
-		if (cookiesString !=null) {
-			String[] cookies = cookiesString.split(";");
-			if (cookies.length > 0) {
-				for (int i=0; i< cookies.length; i++) {
-					Cookie cookie = new Cookie();
-					int equalPos = cookies[i].indexOf('=');
-					cookie.setName(cookies[i].substring(0, equalPos));
-					cookie.setValue(cookies[i].substring(equalPos + 1));
-					cookie.setDomain(serverUri.getHost());	// VERY IMPORTANT 
-					cookie.setPath(serverUri.getPath());	// VERY IMPORTANT
+        // Account Manager
+        AccountManager am = AccountManager.get(context.getApplicationContext());
 
-					client.getState().addCookie(cookie);
-				}
-			}
-		}
-	}
-	
-	/**
-	 * Restore the client cookies from accountName
-	 * @param accountName
-	 * @param client
-	 * @param context
-	 */
-	public static void restoreCookies(String accountName, OwnCloudClient client, Context context) {
-		Log_OC.d(TAG, "Restoring cookies for " + accountName);
+        Uri serverUri = (client.getBaseUri() != null) ? client.getBaseUri() : client.getWebdavUri();
+
+        String cookiesString = am.getUserData(account, Constants.KEY_COOKIES);
+        if (cookiesString != null) {
+            String[] cookies = cookiesString.split(";");
+            if (cookies.length > 0) {
+                for (int i = 0; i < cookies.length; i++) {
+                    Cookie cookie = new Cookie();
+                    int equalPos = cookies[i].indexOf('=');
+                    cookie.setName(cookies[i].substring(0, equalPos));
+                    cookie.setValue(cookies[i].substring(equalPos + 1));
+                    cookie.setDomain(serverUri.getHost());    // VERY IMPORTANT
+                    cookie.setPath(serverUri.getPath());    // VERY IMPORTANT
+
+                    client.getState().addCookie(cookie);
+                }
+            }
+        }
+    }
+
+    /**
+     * Restore the client cookies from accountName
+     *
+     * @param accountName
+     * @param client
+     * @param context
+     */
+    public static void restoreCookies(String accountName, OwnCloudClient client, Context context) {
+        Log_OC.d(TAG, "Restoring cookies for " + accountName);
+
+        // Account Manager
+        AccountManager am = AccountManager.get(context.getApplicationContext());
+
+        // Get account
+        Account account = null;
+        Account accounts[] = am.getAccounts();
+        for (Account a : accounts) {
+            if (a.name.equals(accountName)) {
+                account = a;
+                break;
+            }
+        }
+
+        // Restoring cookies
+        if (account != null) {
+            restoreCookies(account, client, context);
+        }
+    }
 
-		// Account Manager
-		AccountManager am = AccountManager.get(context.getApplicationContext());
-		
-		// Get account
-		Account account = null;
-		Account accounts[] = am.getAccounts();
-		for (Account a : accounts) {
-			if (a.name.equals(accountName)) {
-				account = a;
-				break;
-			}
-		}
-		
-		// Restoring cookies
-		if (account != null) {
-			restoreCookies(account, client, context);
-		}
-	}
-	
     public static class AccountNotFoundException extends AccountsException {
-        
-		/** Generated - should be refreshed every time the class changes!! */
-		private static final long serialVersionUID = -1684392454798508693L;
-        
-        private Account mFailedAccount; 
-                
+
+        /**
+         * Generated - should be refreshed every time the class changes!!
+         */
+        private static final long serialVersionUID = -1684392454798508693L;
+
+        private Account mFailedAccount;
+
         public AccountNotFoundException(Account failedAccount, String message, Throwable cause) {
             super(message, cause);
             mFailedAccount = failedAccount;
         }
-        
+
         public Account getFailedAccount() {
             return mFailedAccount;
         }
     }
 
 
-	public static class Constants {
-	    /**
-	     * Value under this key should handle path to webdav php script. Will be
-	     * removed and usage should be replaced by combining
-	     * {@link com.owncloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and
-	     * {@link com.owncloud.android.lib.resources.status.OwnCloudVersion}
-	     * 
-	     * @deprecated
-	     */
-	    public static final String KEY_OC_URL = "oc_url";
-	    /**
-	     * Version should be 3 numbers separated by dot so it can be parsed by
-	     * {@link com.owncloud.android.lib.resources.status.OwnCloudVersion}
-	     */
-	    public static final String KEY_OC_VERSION = "oc_version";
-	    /**
-	     * Base url should point to owncloud installation without trailing / ie:
-	     * http://server/path or https://owncloud.server
-	     */
-	    public static final String KEY_OC_BASE_URL = "oc_base_url";
-	    /**
-	     * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
-	     */
-	    public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";
-	    /**
-	     * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on.
-	     */
-	    public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso";
-	    /**
-	    * Flag signaling if the ownCloud server supports Share API"
-        * @deprecated
-        */
-	    public static final String KEY_SUPPORTS_SHARE_API = "oc_supports_share_api";
-	    /**
-	     * OC account cookies
-	     */
-	    public static final String KEY_COOKIES = "oc_account_cookies";
+    public static class Constants {
+        /**
+         * Value under this key should handle path to webdav php script. Will be
+         * removed and usage should be replaced by combining
+         * {@link com.owncloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and
+         * {@link com.owncloud.android.lib.resources.status.OwnCloudVersion}
+         *
+         * @deprecated
+         */
+        public static final String KEY_OC_URL = "oc_url";
+        /**
+         * Version should be 3 numbers separated by dot so it can be parsed by
+         * {@link com.owncloud.android.lib.resources.status.OwnCloudVersion}
+         */
+        public static final String KEY_OC_VERSION = "oc_version";
+        /**
+         * Base url should point to owncloud installation without trailing / ie:
+         * http://server/path or https://owncloud.server
+         */
+        public static final String KEY_OC_BASE_URL = "oc_base_url";
+        /**
+         * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
+         */
+        public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";
+        /**
+         * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on.
+         */
+        public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso";
+        /**
+         * Flag signaling if the ownCloud server supports Share API"
+         *
+         * @deprecated
+         */
+        public static final String KEY_SUPPORTS_SHARE_API = "oc_supports_share_api";
+        /**
+         * OC account cookies
+         */
+        public static final String KEY_COOKIES = "oc_account_cookies";
 
         /**
          * OC account version
          */
         public static final String KEY_OC_ACCOUNT_VERSION = "oc_account_version";
 
-		/**
-		 * User's display name
-		 */
-		public static final String KEY_DISPLAY_NAME = "oc_display_name";
+        /**
+         * User's display name
+         */
+        public static final String KEY_DISPLAY_NAME = "oc_display_name";
 
     }
 
diff --git a/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java b/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java
index a37f51a0..00e6f661 100644
--- a/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java
+++ b/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java
@@ -53,6 +53,8 @@ public class OwnCloudVersion implements Comparable<OwnCloudVersion> {
 
     private static final int MINIMUM_VERSION_WITH_NOT_RESHAREABLE_FEDERATED = 0x09010000;   // 9.1
 
+    private static final int MINIMUM_VERSION_WITH_SESSION_MONITORING = 0x09010000;   // 9.1
+
     private static final int MAX_DOTS = 3;
 
     // format is in version
@@ -162,4 +164,8 @@ public class OwnCloudVersion implements Comparable<OwnCloudVersion> {
     public boolean isNotReshareableFederatedSupported() {
         return (mVersion >= MINIMUM_VERSION_WITH_NOT_RESHAREABLE_FEDERATED);
     }
+
+    public boolean isSessionMonitoringSupported() {
+        return (mVersion >= MINIMUM_VERSION_WITH_SESSION_MONITORING);
+    }
 }

From 455cabb72b4abb5aa54447ed9a1543c595255efd Mon Sep 17 00:00:00 2001
From: "David A. Velasco" <dvelasco@owncloud.com>
Date: Thu, 15 Dec 2016 13:54:07 +0100
Subject: [PATCH 2/3] Add new OwnCloudClientManagerFactory implementation to
 get OwnCloudClient instances that keep session or not depending on the server
 version

---
 .../lib/common/DynamicSessionManager.java     |  66 ++++++++++
 .../android/lib/common/OwnCloudAccount.java   |   4 +
 .../common/OwnCloudClientManagerFactory.java  | 114 +++++++++---------
 3 files changed, 129 insertions(+), 55 deletions(-)
 create mode 100644 src/com/owncloud/android/lib/common/DynamicSessionManager.java

diff --git a/src/com/owncloud/android/lib/common/DynamicSessionManager.java b/src/com/owncloud/android/lib/common/DynamicSessionManager.java
new file mode 100644
index 00000000..bfd37af3
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/DynamicSessionManager.java
@@ -0,0 +1,66 @@
+package com.owncloud.android.lib.common;
+
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+
+import com.owncloud.android.lib.common.accounts.AccountUtils;
+import com.owncloud.android.lib.resources.status.OwnCloudVersion;
+
+import java.io.IOException;
+
+/**
+ * Dynamic implementation of {@link OwnCloudClientManager}.
+ *
+ * Wraps instances of {@link SingleSessionManager} and {@link SimpleFactoryManager} and delegates on one
+ * or the other depending on the known version of the server corresponding to the {@link OwnCloudAccount}
+ *
+ * @author David A. Velasco
+ */
+
+public class DynamicSessionManager implements OwnCloudClientManager {
+
+    private SimpleFactoryManager mSimpleFactoryManager = new SimpleFactoryManager();
+
+    private SingleSessionManager mSingleSessionManager = new SingleSessionManager();
+
+    @Override
+    public OwnCloudClient getClientFor(OwnCloudAccount account, Context context)
+        throws AccountUtils.AccountNotFoundException,
+                OperationCanceledException, AuthenticatorException, IOException {
+
+        OwnCloudVersion ownCloudVersion = null;
+        if (account.getSavedAccount() != null) {
+            ownCloudVersion = AccountUtils.getServerVersionForAccount(
+                account.getSavedAccount(), context
+            );
+        }
+
+        if (ownCloudVersion !=  null && ownCloudVersion.isSessionMonitoringSupported()) {
+            return mSingleSessionManager.getClientFor(account, context);
+        } else {
+            return mSimpleFactoryManager.getClientFor(account, context);
+        }
+    }
+
+    @Override
+    public OwnCloudClient removeClientFor(OwnCloudAccount account) {
+        OwnCloudClient clientRemoved = mSimpleFactoryManager.removeClientFor(account);
+        OwnCloudClient clientRemoved2 = mSingleSessionManager.removeClientFor(account);
+        if (clientRemoved2 != null) {
+            return clientRemoved2;
+        } else {
+            return clientRemoved;
+        }
+        // clientRemoved and clientRemoved2 should not be != null at the same time
+    }
+
+    @Override
+    public void saveAllClients(Context context, String accountType)
+        throws AccountUtils.AccountNotFoundException,
+                AuthenticatorException, IOException, OperationCanceledException {
+        mSimpleFactoryManager.saveAllClients(context, accountType);
+        mSingleSessionManager.saveAllClients(context, accountType);
+    }
+
+}
diff --git a/src/com/owncloud/android/lib/common/OwnCloudAccount.java b/src/com/owncloud/android/lib/common/OwnCloudAccount.java
index 89584466..af440e35 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudAccount.java
+++ b/src/com/owncloud/android/lib/common/OwnCloudAccount.java
@@ -139,6 +139,10 @@ public class OwnCloudAccount {
     	return mSavedAccountName;
     }
 
+    public Account getSavedAccount() {
+        return mSavedAccount;
+    }
+
     public String getDisplayName() {
         if (mDisplayName != null && mDisplayName.length() > 0) {
             return mDisplayName;
diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java b/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java
index 6396d09b..fc675411 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java
+++ b/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java
@@ -24,57 +24,61 @@
 package com.owncloud.android.lib.common;
 
 public class OwnCloudClientManagerFactory {
-	
-	public static enum Policy {
-		ALWAYS_NEW_CLIENT,
-		SINGLE_SESSION_PER_ACCOUNT
-	}
-	
-	private static Policy sDefaultPolicy = Policy.ALWAYS_NEW_CLIENT;
-	
-	private static OwnCloudClientManager sDefaultSingleton;
+
+    public static enum Policy {
+        ALWAYS_NEW_CLIENT,
+        SINGLE_SESSION_PER_ACCOUNT,
+        SINGLE_SESSION_PER_ACCOUNT_IF_SERVER_SUPPORTS_SERVER_MONITORING
+    }
+
+    private static Policy sDefaultPolicy = Policy.ALWAYS_NEW_CLIENT;
+
+    private static OwnCloudClientManager sDefaultSingleton;
 
     private static String sUserAgent;
 
-	public static OwnCloudClientManager newDefaultOwnCloudClientManager() {
-		return newOwnCloudClientManager(sDefaultPolicy);
-	}
-	
-	public static OwnCloudClientManager newOwnCloudClientManager(Policy policy) {
-		switch (policy) {
-			case ALWAYS_NEW_CLIENT:
-				return new SimpleFactoryManager();
-				
-			case SINGLE_SESSION_PER_ACCOUNT:
-				return new SingleSessionManager();
-				
-			default:
-				throw new IllegalArgumentException("Unknown policy");
-		}
-	}
-	
-    public static OwnCloudClientManager getDefaultSingleton() {
-    	if (sDefaultSingleton == null) {
-    		sDefaultSingleton = newDefaultOwnCloudClientManager();
-    	}
-    	return sDefaultSingleton;
+    public static OwnCloudClientManager newDefaultOwnCloudClientManager() {
+        return newOwnCloudClientManager(sDefaultPolicy);
     }
-    
+
+    public static OwnCloudClientManager newOwnCloudClientManager(Policy policy) {
+        switch (policy) {
+            case ALWAYS_NEW_CLIENT:
+                return new SimpleFactoryManager();
+
+            case SINGLE_SESSION_PER_ACCOUNT:
+                return new SingleSessionManager();
+
+            case SINGLE_SESSION_PER_ACCOUNT_IF_SERVER_SUPPORTS_SERVER_MONITORING:
+                return new DynamicSessionManager();
+
+            default:
+                throw new IllegalArgumentException("Unknown policy");
+        }
+    }
+
+    public static OwnCloudClientManager getDefaultSingleton() {
+        if (sDefaultSingleton == null) {
+            sDefaultSingleton = newDefaultOwnCloudClientManager();
+        }
+        return sDefaultSingleton;
+    }
+
     public static Policy getDefaultPolicy() {
-    	return sDefaultPolicy;
+        return sDefaultPolicy;
     }
 
     public static void setDefaultPolicy(Policy policy) {
-    	if (policy == null) {
-    		throw new IllegalArgumentException("Default policy cannot be NULL");
-    	}
-    	if (defaultSingletonMustBeUpdated(policy)) {
-    		sDefaultSingleton = null;
-    	}
-    	sDefaultPolicy = policy;
+        if (policy == null) {
+            throw new IllegalArgumentException("Default policy cannot be NULL");
+        }
+        if (defaultSingletonMustBeUpdated(policy)) {
+            sDefaultSingleton = null;
+        }
+        sDefaultPolicy = policy;
     }
 
-    public static void setUserAgent(String userAgent){
+    public static void setUserAgent(String userAgent) {
         sUserAgent = userAgent;
     }
 
@@ -82,19 +86,19 @@ public class OwnCloudClientManagerFactory {
         return sUserAgent;
     }
 
-	private static boolean defaultSingletonMustBeUpdated(Policy policy) {
-		if (sDefaultSingleton == null) {
-			return false;
-		}
-		if (policy == Policy.ALWAYS_NEW_CLIENT && 
-				!(sDefaultSingleton instanceof SimpleFactoryManager)) {
-			return true;
-		}
-		if (policy == Policy.SINGLE_SESSION_PER_ACCOUNT && 
-				!(sDefaultSingleton instanceof SingleSessionManager)) {
-			return true;
-		}
-		return false;
-	}
+    private static boolean defaultSingletonMustBeUpdated(Policy policy) {
+        if (sDefaultSingleton == null) {
+            return false;
+        }
+        if (policy == Policy.ALWAYS_NEW_CLIENT &&
+            !(sDefaultSingleton instanceof SimpleFactoryManager)) {
+            return true;
+        }
+        if (policy == Policy.SINGLE_SESSION_PER_ACCOUNT &&
+            !(sDefaultSingleton instanceof SingleSessionManager)) {
+            return true;
+        }
+        return false;
+    }
 
 }

From b2034e1be22e429e53defadcd22ca6259fd7af50 Mon Sep 17 00:00:00 2001
From: "David A. Velasco" <dvelasco@owncloud.com>
Date: Fri, 16 Dec 2016 09:45:25 +0100
Subject: [PATCH 3/3] Rename confusing vars after CR

---
 .../android/lib/common/DynamicSessionManager.java      | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/com/owncloud/android/lib/common/DynamicSessionManager.java b/src/com/owncloud/android/lib/common/DynamicSessionManager.java
index bfd37af3..e478f383 100644
--- a/src/com/owncloud/android/lib/common/DynamicSessionManager.java
+++ b/src/com/owncloud/android/lib/common/DynamicSessionManager.java
@@ -45,12 +45,12 @@ public class DynamicSessionManager implements OwnCloudClientManager {
 
     @Override
     public OwnCloudClient removeClientFor(OwnCloudAccount account) {
-        OwnCloudClient clientRemoved = mSimpleFactoryManager.removeClientFor(account);
-        OwnCloudClient clientRemoved2 = mSingleSessionManager.removeClientFor(account);
-        if (clientRemoved2 != null) {
-            return clientRemoved2;
+        OwnCloudClient clientRemovedFromFactoryManager = mSimpleFactoryManager.removeClientFor(account);
+        OwnCloudClient clientRemovedFromSingleSessionManager = mSingleSessionManager.removeClientFor(account);
+        if (clientRemovedFromSingleSessionManager != null) {
+            return clientRemovedFromSingleSessionManager;
         } else {
-            return clientRemoved;
+            return clientRemovedFromFactoryManager;
         }
         // clientRemoved and clientRemoved2 should not be != null at the same time
     }