<activity\r
android:name=".ui.activity.AuthenticatorActivity"\r
android:exported="true"\r
- android:theme="@style/Theme.ownCloud.noActionBar" >\r
+ android:theme="@style/Theme.ownCloud.noActionBar" \r
+ android:launchMode="singleTask">\r
+ <intent-filter>\r
+ <action android:name="android.intent.action.VIEW" />\r
+\r
+ <category android:name="android.intent.category.DEFAULT" />\r
+ <category android:name="android.intent.category.BROWSABLE" />\r
+\r
+ <data android:scheme="oauth-mobile-app" />\r
+ </intent-filter>\r
</activity>\r
\r
<service android:name=".files.services.FileDownloader" >\r
<service android:name=".files.services.FileUploader" >\r
</service>
- <service android:name=".files.services.InstantUploadService" />
<receiver android:name=".files.InstantUploadBroadcastReceiver">\r
<intent-filter>\r
<action android:name="com.android.camera.NEW_PICTURE" />\r
<action android:name="android.intent.action.BOOT_COMPLETED"/>\r
</intent-filter>\r
</receiver>\r
- <service android:name=".files.services.FileObserverService"/>
+ <service android:name=".files.services.FileObserverService"/>\r
+ \r
+ <service android:name=".authenticator.oauth2.services.OAuth2GetTokenService" >\r
+ </service>\r
+
</application>\r
\r
</manifest>
android:visibility="invisible" />
</LinearLayout>
+ <CheckBox
+ android:id="@+id/oauth_onOff_check"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:checked="false"
+ android:onClick="onOff_check_Click"
+ android:text="@string/oauth_check_onoff"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
<EditText
+ android:id="@+id/oAuth_URL"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ems="10"
+ android:hint="@string/oauth_host_url"
+ android:singleLine="true"
+ android:visibility="gone" >
+
+ <requestFocus />
+ </EditText>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <ImageView
+ android:id="@+id/auth2_action_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dp"
+ android:layout_marginRight="5dp"
+ android:src="@android:drawable/stat_notify_sync"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/oauth2_status_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="TextView"
+ android:visibility="gone" />
+ </LinearLayout>
+
+ <EditText
android:id="@+id/account_username"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_height="wrap_content"
android:text="TextView"
android:visibility="invisible" />
-
</LinearLayout>
+ <CheckBox
+ android:id="@+id/oauth_onOff_check"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:checked="false"
+ android:onClick="onOff_check_Click"
+ android:text="@string/oauth_check_onoff"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_weight="1"
android:text="@string/auth_login_details"
android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <EditText
+ android:id="@+id/oAuth_URL"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ems="10"
+ android:hint="@string/oauth_host_url"
+ android:singleLine="true"
+ android:visibility="gone" >
+
+ <requestFocus />
+ </EditText>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" >
+
+ <ImageView
+ android:id="@+id/auth2_action_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dp"
+ android:layout_marginRight="5dp"
+ android:src="@android:drawable/stat_notify_sync"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/oauth2_status_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="TextView"
+ android:visibility="gone" />
+ </LinearLayout>
<EditText
android:id="@+id/account_username"
android:layout_width="match_parent"
<string name="filedisplay_unexpected_bad_get_content">"Unexpected problem ; please select the file from a different app"</string>
<string name="filedisplay_no_file_selected">No file was selected</string>
+ <string name="oauth_host_url">oAuth2 URL</string>
+ <string name="oauth_check_onoff">Login with oAuth2.</string>
+ <string name="oauth_login_connection">Connecting to oAuth2 server…</string>
+ <string name="oauth_code_validation_message">Please, open a web browser and go to:\n%1$s.\nValidate this code there:\n%2$s</string>
+ <string name="oauth_connection_url_unavailable">Connection to this URL not available.</string>
+
<string name="ssl_validator_title">Warning</string>
<string name="ssl_validator_header">The identity of the site could not be verified</string>
<string name="ssl_validator_reason_cert_not_trusted">- The server certificate is not trusted</string>
<item name="android:singleLine">true</item>
</style>
+
+ <style name="OAuthDialog" parent="@android:style/Theme.Dialog">
+ <item name="android:windowNoTitle">false</item>
+ </style>
<color name="setup_text_hint">#777777</color>
<color name="setup_text_typed">#000000</color>
public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php";\r
public static final String WEBDAV_PATH_2_0 = "/files/webdav.php";\r
public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav";\r
+ private static final String ODAV_PATH = "/remote.php/odav";\r
public static final String CARDDAV_PATH_2_0 = "/apps/contacts/carddav.php";\r
public static final String CARDDAV_PATH_4_0 = "/remote/carddav.php";\r
public static final String STATUS_PATH = "/status.php";\r
* @param version version of owncloud\r
* @return webdav path for given OC version, null if OC version unknown\r
*/\r
- public static String getWebdavPath(OwnCloudVersion version) {\r
+ public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth) {\r
+ if (supportsOAuth) {\r
+ return ODAV_PATH;\r
+ }\r
if (version != null) {\r
if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)\r
return WEBDAV_PATH_4_0;\r
AccountManager ama = AccountManager.get(context);\r
String baseurl = ama.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL);\r
String strver = ama.getUserData(account, AccountAuthenticator.KEY_OC_VERSION);\r
+ boolean supportsOAuth = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
OwnCloudVersion ver = new OwnCloudVersion(strver);\r
- String webdavpath = getWebdavPath(ver);\r
+ String webdavpath = getWebdavPath(ver, supportsOAuth);\r
\r
if (webdavpath == null) return null;\r
return baseurl + webdavpath;\r
import com.owncloud.android.datamodel.FileDataStorageManager;\r
import com.owncloud.android.datamodel.OCFile;\r
import com.owncloud.android.files.services.FileUploader;\r
-import com.owncloud.android.network.OwnCloudClientUtils;\r
\r
import android.accounts.Account;\r
import android.accounts.AccountManager;\r
import android.widget.Toast;\r
\r
import com.owncloud.android.R;\r
-import eu.alefzero.webdav.WebdavClient;\r
\r
/**\r
* This can be used to upload things to an ownCloud instance.\r
\r
public void uploadFiles() {\r
try {\r
+ /* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed \r
WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());\r
-\r
// create last directory in path if necessary\r
if (mCreateDir) {\r
wdc.createDirectory(mUploadPath);\r
}\r
+ */\r
\r
String[] local = new String[mStreamsToUpload.size()], remote = new String[mStreamsToUpload.size()];\r
\r
*/\r
public static final String ACCOUNT_TYPE = "owncloud";\r
public static final String AUTH_TOKEN_TYPE = "org.owncloud";\r
+ public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password";\r
+ public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token";\r
+ public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token";\r
\r
public static final String KEY_AUTH_TOKEN_TYPE = "authTokenType";\r
public static final String KEY_REQUIRED_FEATURES = "requiredFeatures";\r
* http://server/path or https://owncloud.server\r
*/\r
public static final String KEY_OC_BASE_URL = "oc_base_url";\r
-\r
- private static final String TAG = "AccountAuthenticator";\r
+ /**\r
+ * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.\r
+ */\r
+ public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";\r
+ \r
+ private static final String TAG = AccountAuthenticator.class.getSimpleName();\r
+ \r
private Context mContext;\r
\r
public AccountAuthenticator(Context context) {\r
return e.getFailureBundle();\r
}\r
final AccountManager am = AccountManager.get(mContext);\r
- final String password = am.getPassword(account);\r
- if (password != null) {\r
- final Bundle result = new Bundle();\r
- result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);\r
- result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);\r
- result.putString(AccountManager.KEY_AUTHTOKEN, password);\r
- return result;\r
+ if (authTokenType.equals(AUTH_TOKEN_TYPE_ACCESS_TOKEN)) {\r
+ final String accessToken = am.peekAuthToken(account, authTokenType);\r
+ if (accessToken != null) {\r
+ final Bundle result = new Bundle();\r
+ result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);\r
+ result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);\r
+ result.putString(AccountManager.KEY_AUTHTOKEN, accessToken);\r
+ return result;\r
+ }\r
+ \r
+ } else if (authTokenType.equals(AUTH_TOKEN_TYPE_PASSWORD)) {\r
+ final String password = am.getPassword(account);\r
+ if (password != null) {\r
+ final Bundle result = new Bundle();\r
+ result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);\r
+ result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);\r
+ result.putString(AccountManager.KEY_AUTHTOKEN, password);\r
+ return result;\r
+ }\r
}\r
\r
final Intent intent = new Intent(mContext, AuthenticatorActivity.class);\r
response);\r
intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);\r
intent.putExtra(KEY_LOGIN_OPTIONS, options);\r
- intent.putExtra(AuthenticatorActivity.PARAM_USERNAME, account.name);\r
+ intent.putExtra(AuthenticatorActivity.PARAM_USERNAME, account.name); // TODO fix, this will pass the accountName, not the username\r
\r
final Bundle bundle = new Bundle();\r
bundle.putParcelable(AccountManager.KEY_INTENT, intent);\r
\r
private void setIntentFlags(Intent intent) {\r
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
- intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);\r
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);\r
+ //intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);\r
+ //intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); // incompatible with the authorization code grant in OAuth\r
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r
intent.addFlags(Intent.FLAG_FROM_BACKGROUND);\r
}\r
\r
private void validateAuthTokenType(String authTokenType)\r
throws UnsupportedAuthTokenTypeException {\r
- if (!authTokenType.equals(AUTH_TOKEN_TYPE)) {\r
+ if (!authTokenType.equals(AUTH_TOKEN_TYPE) &&\r
+ !authTokenType.equals(AUTH_TOKEN_TYPE_PASSWORD) &&\r
+ !authTokenType.equals(AUTH_TOKEN_TYPE_ACCESS_TOKEN) &&\r
+ !authTokenType.equals(AUTH_TOKEN_TYPE_REFRESH_TOKEN) ) {\r
throw new UnsupportedAuthTokenTypeException();\r
}\r
}\r
--- /dev/null
+package com.owncloud.android.authenticator.oauth2;
+
+/**
+ * Class used to store data from the app registration in oAuth2 server.
+ * THIS VALUES ARE ORIENTATIVE.
+ * MUST BE CHANGED WITH THE CORRECT ONES.
+ *
+ * @author SolidGear S.L.
+ *
+ */
+
+public class OAuth2Context {
+
+ 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";
+}
--- /dev/null
+package com.owncloud.android.authenticator.oauth2;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.message.BasicNameValuePair;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.util.Log;
+
+import com.owncloud.android.authenticator.oauth2.OnOAuth2GetCodeResultListener.ResultOAuthType;
+import com.owncloud.android.authenticator.oauth2.connection.ConnectorOAuth2;
+
+/**
+ * Implements the communication with oAuth2 server to get User Code and other useful values.
+ *
+ * @author SolidGear S.L.
+ *
+ */
+public class OAuth2GetCodeRunnable implements Runnable {
+
+ public static final String CODE_USER_CODE = "user_code";
+ public static final String CODE_CLIENT_ID = "client_id";
+ public static final String CODE_SCOPE = "scope";
+ public static final String CODE_VERIFICATION_URL = "verification_url";
+ public static final String CODE_EXPIRES_IN = "expires_in";
+ public static final String CODE_DEVICE_CODE = "device_code";
+ public static final String CODE_INTERVAL = "interval";
+
+ private static final String CODE_RESPONSE_TYPE = "response_type";
+ private static final String CODE_REDIRECT_URI = "redirect_uri";
+
+ private String mGrantType = OAuth2Context.OAUTH2_AUTH_CODE_GRANT_TYPE;
+
+ private static final String TAG = "OAuth2GetCodeRunnable";
+ private OnOAuth2GetCodeResultListener mListener;
+ private String mUrl;
+ private Handler mHandler;
+ private Context mContext;
+ private JSONObject codeResponseJson = null;
+ ResultOAuthType mLatestResult;
+
+
+ public void setListener(OnOAuth2GetCodeResultListener listener, Handler handler) {
+ mListener = listener;
+ mHandler = handler;
+ }
+
+ public OAuth2GetCodeRunnable(String url, Context context) {
+ mListener = null;
+ mHandler = null;
+ mUrl = url;
+ mContext = context;
+ }
+
+ @Override
+ public void run() {
+
+ if (!isOnline()) {
+ postResult(ResultOAuthType.NO_NETWORK_CONNECTION,null);
+ return;
+ }
+
+ if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) {
+ mLatestResult = (mUrl.startsWith("https://"))? ResultOAuthType.OK_SSL : ResultOAuthType.OK_NO_SSL;
+ } else {
+ mUrl = "https://" + mUrl;
+ mLatestResult = ResultOAuthType.OK_SSL;
+ }
+
+ if (mGrantType.equals(OAuth2Context.OAUTH2_AUTH_CODE_GRANT_TYPE)) {
+ requestBrowserToGetAuthorizationCode();
+
+ } else if (mGrantType.equals(OAuth2Context.OAUTH_G_DEVICE_GETTOKEN_GRANT_TYPE)) {
+ getAuthorizationCode();
+ }
+ }
+
+ /// open the authorization endpoint in a web browser!
+ private void requestBrowserToGetAuthorizationCode() {
+ Uri uri = Uri.parse(mUrl);
+ Uri.Builder uriBuilder = uri.buildUpon();
+ uriBuilder.appendQueryParameter(CODE_RESPONSE_TYPE, OAuth2Context.OAUTH2_CODE_RESPONSE_TYPE);
+ uriBuilder.appendQueryParameter(CODE_REDIRECT_URI, OAuth2Context.MY_REDIRECT_URI);
+ uriBuilder.appendQueryParameter(CODE_CLIENT_ID, OAuth2Context.OAUTH2_F_CLIENT_ID);
+ uriBuilder.appendQueryParameter(CODE_SCOPE, OAuth2Context.OAUTH2_F_SCOPE);
+ //uriBuilder.appendQueryParameter(CODE_STATE, whateverwewant);
+
+ uri = uriBuilder.build();
+ Log.d(TAG, "Starting browser to view " + uri.toString());
+
+ Intent i = new Intent(Intent.ACTION_VIEW, uri);
+ mContext.startActivity(i);
+
+ postResult(mLatestResult, null);
+ }
+
+
+ private void getAuthorizationCode() {
+ ConnectorOAuth2 connectorOAuth2 = new ConnectorOAuth2(mUrl);
+ try {
+ List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
+ nameValuePairs.add(new BasicNameValuePair(CODE_CLIENT_ID, OAuth2Context.OAUTH2_G_DEVICE_CLIENT_ID));
+ nameValuePairs.add(new BasicNameValuePair(CODE_SCOPE,OAuth2Context.OAUTH2_G_DEVICE_GETCODE_SCOPES));
+ UrlEncodedFormEntity params = new UrlEncodedFormEntity(nameValuePairs);
+ codeResponseJson = new JSONObject(connectorOAuth2.connPost(params));
+ } catch (JSONException e) {
+ Log.e(TAG, "JSONException converting to Json: " + e.toString());
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "UnsupportedEncodingException encoding URL values: " + e.toString());
+ } catch (Exception e) {
+ Log.e(TAG, "Exception : " + e.toString());
+ }
+
+ if (codeResponseJson == null) {
+ mLatestResult = ResultOAuthType.HOST_NOT_AVAILABLE;
+ }
+ postResult(mLatestResult, codeResponseJson);
+ }
+
+
+ private boolean isOnline() {
+ ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ return cm != null && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnectedOrConnecting();
+ }
+
+ private void postResult(final ResultOAuthType result,final JSONObject codeResponseJson) {
+ if (mHandler != null && mListener != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onOAuth2GetCodeResult(result, codeResponseJson);
+ }
+ });
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package com.owncloud.android.authenticator.oauth2;
+
+import org.json.JSONObject;
+
+/**
+ * Listener that expects results from OAuth2GetCodeRunnable class.
+ *
+ * @author SolidGear S.L.
+ *
+ */
+public interface OnOAuth2GetCodeResultListener {
+
+ enum ResultOAuthType {
+ OK_SSL, OK_NO_SSL, SSL_INIT_ERROR, HOST_NOT_AVAILABLE, TIMEOUT, NO_NETWORK_CONNECTION, INCORRECT_ADDRESS, INSTANCE_NOT_CONFIGURED, FILE_NOT_FOUND, UNKNOWN_ERROR, WRONG_CONNECTION, SSL_UNVERIFIED_SERVER, BAD_OC_VERSION
+ }
+
+ public void onOAuth2GetCodeResult(ResultOAuthType type, JSONObject code);
+
+}
--- /dev/null
+package com.owncloud.android.authenticator.oauth2.connection;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.params.CookiePolicy;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+
+import android.util.Log;
+
+/**
+ * Implements HTTP POST communications with an oAuth2 server.
+ *
+ * @author SolidGear S.L.
+ *
+ */
+public class ConnectorOAuth2 {
+
+ private static final String TAG = "ConnectorOAuth2";
+ /** Maximum time to wait for a response from the server when the connection is being tested, in MILLISECONDs. */
+ private static final int TRY_CONNECTION_TIMEOUT = 5000;
+
+ private DefaultHttpClient httpClient;
+ private HttpContext localContext;
+ private String ConnectorOAuth2Url;
+
+ public ConnectorOAuth2 (String destUrl) {
+ prepareConn();
+ setConnectorOAuth2Url(destUrl);
+ }
+
+ public ConnectorOAuth2 () {
+ prepareConn();
+ }
+
+ public String getConnectorOAuth2Url() {
+ return ConnectorOAuth2Url;
+ }
+
+ public void setConnectorOAuth2Url(String connectorOAuth2Url) {
+ ConnectorOAuth2Url = connectorOAuth2Url;
+ }
+
+ /**
+ * Starts the communication with the server.
+ *
+ * @param UrlEncodedFormEntity : parameters included in the POST call.
+ * @return String : data returned from the server in String format.
+ */
+
+ public String connPost(UrlEncodedFormEntity data) {
+ String dataOut = null;
+ HttpPost httpPost = null;
+ int responseCode = -1;
+ HttpResponse response = null;
+
+ httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2109);
+
+ if (ConnectorOAuth2Url == null) {
+ Log.e(TAG, "connPost error: destination URI could not be null");
+ return null;
+ }
+
+ if (data == null){
+ Log.e(TAG, "connPost error: data to send to URI " + ConnectorOAuth2Url + "could not be null");
+ return null;
+ }
+
+ httpPost = new HttpPost(ConnectorOAuth2Url);
+
+ httpPost.setHeader("Accept","text/html,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
+ httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
+ httpPost.setEntity((UrlEncodedFormEntity) data);
+
+ try {
+ response = httpClient.execute(httpPost,localContext);
+
+ if (response == null) {
+ Log.e(TAG, "connPost error: response from uri " + ConnectorOAuth2Url + " is null");
+ return null;
+ }
+
+ responseCode = response.getStatusLine().getStatusCode();
+
+ if ((responseCode != 200)) {
+ Log.e(TAG, "connPost error: response from uri "+ ConnectorOAuth2Url + " returns status " + responseCode);
+ return null;
+ }
+
+ dataOut = EntityUtils.toString(response.getEntity());
+
+ } catch (Exception e) {
+ Log.e(TAG, "connPost Exception: " + e);
+ }
+
+ return dataOut;
+ }
+
+ private void prepareConn () {
+ HttpParams localParams = new BasicHttpParams();
+ HttpConnectionParams.setConnectionTimeout(localParams, TRY_CONNECTION_TIMEOUT);
+ HttpConnectionParams.setSoTimeout(localParams, TRY_CONNECTION_TIMEOUT);
+ httpClient = new DefaultHttpClient(localParams);
+ localContext = new BasicHttpContext();
+ }
+}
--- /dev/null
+package com.owncloud.android.authenticator.oauth2.services;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.message.BasicNameValuePair;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.owncloud.android.authenticator.oauth2.OAuth2Context;
+import com.owncloud.android.authenticator.oauth2.connection.ConnectorOAuth2;
+
+/**
+ * Service class that implements the second communication with the oAuth2 server:
+ * pooling for the token in an interval. It send a broadcast with the results when are positive;
+ * otherwise, it continues asking to the server.
+ *
+ * @author Solid Gear S.L.
+ *
+ */
+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_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";
+ public static final String TOKEN_RECEIVED_ERROR_AUTH_TOKEN = "authorization_pending";
+ public static final String TOKEN_RECEIVED_ERROR_SLOW_DOWN = "slow_down";
+ public static final String TOKEN_ACCESS_TOKEN = "access_token";
+ public static final String TOKEN_TOKEN_TYPE = "token_type";
+ public static final String TOKEN_EXPIRES_IN = "expires_in";
+ public static final String TOKEN_REFRESH_TOKEN = "refresh_token";
+
+ private String requestDeviceCode;
+ private int requestInterval = -1;
+ private String requestBaseURI;
+ private ConnectorOAuth2 connectorOAuth2;
+ private static final String TAG = "OAuth2GetTokenService";
+ private Timer timer = new Timer();
+
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Bundle param = intent.getExtras();
+
+ if (param != null) {
+ 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, "onStartCommand -> requestDeviceCode=" + requestDeviceCode);
+ Log.d(TAG, "onStartCommand -> requestInterval=" + requestInterval);
+ } else {
+ Log.e(TAG, "onStartCommand -> params could not be null");
+ }
+ startService();
+ return Service.START_NOT_STICKY;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ shutdownService();
+ }
+
+ private void startService() {
+ final UrlEncodedFormEntity params = prepareComm();
+ timer.scheduleAtFixedRate(
+ new TimerTask() {
+ public void run() {
+ requestToken(params);
+ }
+ }, 0, requestInterval * 1000);
+ Log.d(TAG, "startService -> Timer started");
+ }
+
+ private void shutdownService() {
+ if (timer != null) timer.cancel();
+ Log.d(TAG, "shutdownService -> Timer stopped");
+ }
+
+
+ private UrlEncodedFormEntity prepareComm() {
+
+ UrlEncodedFormEntity params = null;
+ connectorOAuth2 = new ConnectorOAuth2();
+
+ if (requestBaseURI == null || requestBaseURI.trim().equals("")) {
+ Log.e(TAG, "run -> request URI could not be null");
+ postResult(null);
+ }
+
+ if (requestInterval == -1) {
+ Log.e(TAG, "run -> request Interval must have valid positive value");
+ postResult(null);
+ }
+
+ if (requestDeviceCode == null || requestDeviceCode.trim().equals("")) {
+ Log.e(TAG, "run -> request DeviceCode could not be null");
+ postResult(null);
+ }
+
+ try{
+ connectorOAuth2.setConnectorOAuth2Url(requestBaseURI + OAuth2Context.OAUTH2_G_DEVICE_GETTOKEN_URL);
+
+ List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
+ 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_G_DEVICE_GETTOKEN_GRANT_TYPE));
+
+ params = new UrlEncodedFormEntity(nameValuePairs);
+ }
+ catch (Exception ex1){
+ Log.w(TAG, ex1.toString());
+ postResult(null);
+ }
+
+ return params;
+ }
+
+ protected void requestToken(UrlEncodedFormEntity params){
+ JSONObject tokenJson = null;
+ String error = null;
+ HashMap<String, String> resultTokenMap;
+
+ String tokenResponse = connectorOAuth2.connPost(params);
+
+ try {
+ tokenJson = new JSONObject(tokenResponse);
+ } catch (JSONException e) {
+ Log.e(TAG, "Exception converting to Json " + e.toString());
+ }
+
+ try {
+ // We try to get error string.
+ if (tokenJson.has(TOKEN_RECEIVED_ERROR)) {
+ error = tokenJson.getString(TOKEN_RECEIVED_ERROR);
+ Log.d(TAG, "requestToken -> Obtained error "+ error);
+ } else {
+ //We have got the token. Parse the answer.
+ resultTokenMap = parseResult(tokenJson);
+ postResult(resultTokenMap);
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "Exception converting to Json " + e.toString());
+ }
+ }
+
+ private HashMap<String, String> parseResult (JSONObject tokenJson) {
+ HashMap<String, String> resultTokenMap=new HashMap<String, String>();
+
+ try {
+ resultTokenMap.put(TOKEN_ACCESS_TOKEN, tokenJson.getString(TOKEN_ACCESS_TOKEN));
+ resultTokenMap.put(TOKEN_TOKEN_TYPE, tokenJson.getString(TOKEN_TOKEN_TYPE));
+ resultTokenMap.put(TOKEN_EXPIRES_IN, tokenJson.getString(TOKEN_EXPIRES_IN));
+ resultTokenMap.put(TOKEN_REFRESH_TOKEN, tokenJson.getString(TOKEN_REFRESH_TOKEN));
+ } catch (JSONException e) {
+ Log.e(TAG, "parseResult: Exception converting to Json " + e.toString());
+ }
+ return resultTokenMap;
+ }
+
+ /**
+ * Returns obtained values with a broadcast.
+ *
+ * @param tokenResponse : obtained values.
+ */
+ private void postResult(HashMap<String, String> tokenResponse) {
+ Intent intent = new Intent(TOKEN_RECEIVED_MESSAGE);
+ intent.putExtra(TOKEN_RECEIVED_DATA,tokenResponse);
+ sendBroadcast(intent);
+ shutdownService();
+ }
+}
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.network.OwnCloudClientUtils;
import com.owncloud.android.operations.RemoteOperationResult;
import com.owncloud.android.operations.SynchronizeFileOperation;
import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
-import eu.alefzero.webdav.WebdavClient;
import android.accounts.Account;
import android.content.Context;
mPath);
return;
}
- WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mOCAccount, mContext);
FileDataStorageManager storageManager = new FileDataStorageManager(mOCAccount, mContext.getContentResolver());
OCFile file = storageManager.getFileByLocalPath(mPath); // a fresh object is needed; many things could have occurred to the file since it was registered to observe
// again, assuming that local files are linked to a remote file AT MOST, SOMETHING TO BE DONE;
true,
true,
mContext);
- RemoteOperationResult result = sfo.execute(wc);
+ RemoteOperationResult result = sfo.execute(mOCAccount, mContext);
if (result.getCode() == ResultCode.SYNC_CONFLICT) {
// ISSUE 5: if the user is not running the app (this is a service!), this can be very intrusive; a notification should be preferred
Intent i = new Intent(mContext, ConflictsResolveActivity.class);
package com.owncloud.android.files.services;\r
\r
import java.io.File;\r
+import java.io.IOException;\r
import java.util.AbstractList;\r
import java.util.Iterator;\r
import java.util.Vector;\r
import com.owncloud.android.ui.fragment.FileDetailFragment;\r
\r
import android.accounts.Account;\r
+import android.accounts.AccountsException;\r
import android.app.Notification;\r
import android.app.NotificationManager;\r
import android.app.PendingIntent;\r
\r
notifyDownloadStart(mCurrentDownload);\r
\r
- /// prepare client object to send the request to the ownCloud server\r
- if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {\r
- mLastAccount = mCurrentDownload.getAccount();\r
- mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());\r
- mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());\r
- }\r
-\r
- /// perform the download\r
RemoteOperationResult downloadResult = null;\r
try {\r
- downloadResult = mCurrentDownload.execute(mDownloadClient);\r
+ /// prepare client object to send the request to the ownCloud server\r
+ if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {\r
+ mLastAccount = mCurrentDownload.getAccount();\r
+ mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());\r
+ mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());\r
+ }\r
+\r
+ /// perform the download\r
+ if (downloadResult == null) {\r
+ downloadResult = mCurrentDownload.execute(mDownloadClient);\r
+ }\r
if (downloadResult.isSuccess()) {\r
saveDownloadedFile();\r
}\r
\r
+ } catch (AccountsException e) {\r
+ Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);\r
+ downloadResult = new RemoteOperationResult(e);\r
+ } catch (IOException e) {\r
+ Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);\r
+ downloadResult = new RemoteOperationResult(e);\r
+ \r
} finally {\r
synchronized(mPendingDownloads) {\r
mPendingDownloads.remove(downloadKey);\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-package com.owncloud.android.files.services;
-
-import java.io.File;
-
-import com.owncloud.android.AccountUtils;
-import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.network.OwnCloudClientUtils;
-
-import android.accounts.Account;
-import android.content.Context;
-import eu.alefzero.webdav.WebdavClient;
-
-public class FileOperation {
-
- Context mContext;
-
- public FileOperation(Context contex){
- this.mContext = contex;
- }
-
- /**
- * Deletes a file from ownCloud - locally and remote.
- * @param file The file to delete
- * @return True on success, otherwise false
- */
- public boolean delete(OCFile file){
-
- Account account = AccountUtils.getCurrentOwnCloudAccount(mContext);
- WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(account, mContext);
- if(client.deleteFile(file.getRemotePath())){
- File localFile = new File(file.getStoragePath());
- return localFile.delete();
- }
-
- return false;
- }
-
-}
package com.owncloud.android.files.services;
import java.io.File;
+import java.io.IOException;
import java.util.AbstractList;
import java.util.Iterator;
import java.util.Vector;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.InstantUploadBroadcastReceiver;
import com.owncloud.android.operations.ChunkedUploadFileOperation;
+import com.owncloud.android.operations.CreateFolderOperation;
+import com.owncloud.android.operations.RemoteOperation;
import com.owncloud.android.operations.RemoteOperationResult;
import com.owncloud.android.operations.UploadFileOperation;
import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.accounts.AccountsException;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
notifyUploadStart(mCurrentUpload);
+ RemoteOperationResult uploadResult = null;
- /// prepare client object to send requests to the ownCloud server
- if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) {
- mLastAccount = mCurrentUpload.getAccount();
- mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());
- mUploadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());
- }
+ try {
+ /// prepare client object to send requests to the ownCloud server
+ if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) {
+ mLastAccount = mCurrentUpload.getAccount();
+ mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());
+ mUploadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());
+ }
- /// create remote folder for instant uploads
- if (mCurrentUpload.isRemoteFolderToBeCreated()) {
- mUploadClient.createDirectory(InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR); // ignoring result; fail could just mean that it already exists, but local database is not synchronized; the upload will be tried anyway
- }
+ /// create remote folder for instant uploads
+ if (mCurrentUpload.isRemoteFolderToBeCreated()) {
+ RemoteOperation operation = new CreateFolderOperation( InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR,
+ mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR).getFileId(), // TODO generalize this : INSTANT_UPLOAD_DIR could not be a child of root
+ mStorageManager);
+ operation.execute(mUploadClient); // ignoring result; fail could just mean that it already exists, but local database is not synchronized; the upload will be tried anyway
+ }
- /// perform the upload
- RemoteOperationResult uploadResult = null;
- try {
+ /// perform the upload
uploadResult = mCurrentUpload.execute(mUploadClient);
if (uploadResult.isSuccess()) {
saveUploadedFile();
}
+ } catch (AccountsException e) {
+ Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+ uploadResult = new RemoteOperationResult(e);
+
+ } catch (IOException e) {
+ Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+ uploadResult = new RemoteOperationResult(e);
+
} finally {
synchronized(mPendingUploads) {
mPendingUploads.remove(uploadKey);
}
}
-
+
/// notify result
notifyUploadResult(uploadResult, mCurrentUpload);
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.files.services;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-
-import com.owncloud.android.network.OwnCloudClientUtils;
-
-import eu.alefzero.webdav.WebdavClient;
-
-import android.accounts.Account;
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.Log;
-
-public class InstantUploadService extends Service {
-
- public static String KEY_FILE_PATH = "KEY_FILEPATH";
- public static String KEY_FILE_SIZE = "KEY_FILESIZE";
- public static String KEY_MIME_TYPE = "KEY_MIMETYPE";
- public static String KEY_DISPLAY_NAME = "KEY_FILENAME";
- public static String KEY_ACCOUNT = "KEY_ACCOUNT";
-
- private static String TAG = "InstantUploadService";
- private static String INSTANT_UPLOAD_DIR = "/InstantUpload";
- private UploaderRunnable mUploaderRunnable;
-
- @Override
- public IBinder onBind(Intent arg0) {
- return null;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (intent == null ||
- !intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_DISPLAY_NAME) ||
- !intent.hasExtra(KEY_FILE_PATH) || !intent.hasExtra(KEY_FILE_SIZE) ||
- !intent.hasExtra(KEY_MIME_TYPE)) {
- Log.w(TAG, "Not all required information was provided, abording");
- return Service.START_NOT_STICKY;
- }
-
- if (mUploaderRunnable == null) {
- mUploaderRunnable = new UploaderRunnable();
- }
-
- String filename = intent.getStringExtra(KEY_DISPLAY_NAME);
- String filepath = intent.getStringExtra(KEY_FILE_PATH);
- String mimetype = intent.getStringExtra(KEY_MIME_TYPE);
- Account account = intent.getParcelableExtra(KEY_ACCOUNT);
- long filesize = intent.getLongExtra(KEY_FILE_SIZE, -1);
-
- mUploaderRunnable.addElementToQueue(filename, filepath, mimetype, filesize, account);
-
- // starting new thread for new download doesnt seems like a good idea
- // maybe some thread pool or single background thread would be better
- Log.d(TAG, "Starting instant upload thread");
- new Thread(mUploaderRunnable).start();
-
- return Service.START_STICKY;
- }
-
- private class UploaderRunnable implements Runnable {
-
- Object mLock;
- List<HashMap<String, Object>> mHashMapList;
-
- public UploaderRunnable() {
- mHashMapList = new LinkedList<HashMap<String, Object>>();
- mLock = new Object();
- }
-
- public void addElementToQueue(String filename,
- String filepath,
- String mimetype,
- long length,
- Account account) {
- HashMap<String, Object> new_map = new HashMap<String, Object>();
- new_map.put(KEY_ACCOUNT, account);
- new_map.put(KEY_DISPLAY_NAME, filename);
- new_map.put(KEY_FILE_PATH, filepath);
- new_map.put(KEY_MIME_TYPE, mimetype);
- new_map.put(KEY_FILE_SIZE, length);
-
- synchronized (mLock) {
- mHashMapList.add(new_map);
- }
- }
-
- private HashMap<String, Object> getFirstObject() {
- synchronized (mLock) {
- if (mHashMapList.size() == 0)
- return null;
- HashMap<String, Object> ret = mHashMapList.get(0);
- mHashMapList.remove(0);
- return ret;
- }
- }
-
- public void run() {
- HashMap<String, Object> working_map;
-
- while ((working_map = getFirstObject()) != null) {
- Account account = (Account) working_map.get(KEY_ACCOUNT);
- String filename = (String) working_map.get(KEY_DISPLAY_NAME);
- String filepath = (String) working_map.get(KEY_FILE_PATH);
- String mimetype = (String) working_map.get(KEY_MIME_TYPE);
-
- WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(account, getApplicationContext());
-
- wdc.createDirectory(INSTANT_UPLOAD_DIR); // fail could just mean that it already exists; put will be tried anyway
- try {
- wdc.putFile(filepath, INSTANT_UPLOAD_DIR + "/" + filename, mimetype);
- } catch (Exception e) {
- // nothing to do; this service is deprecated, indeed
- }
- }
- }
- }
-
-}
--- /dev/null
+/* 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.network;
+
+import java.util.Map;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.auth.AuthChallengeParser;
+import org.apache.commons.httpclient.auth.AuthScheme;
+import org.apache.commons.httpclient.auth.AuthenticationException;
+import org.apache.commons.httpclient.auth.InvalidCredentialsException;
+import org.apache.commons.httpclient.auth.MalformedChallengeException;
+
+import android.util.Log;
+
+/**
+ * Bearer authentication scheme as defined in RFC 6750.
+ *
+ * @author David A. Velasco
+ */
+
+public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ {
+
+ private static final String TAG = BearerAuthScheme.class.getSimpleName();
+
+ public static final String AUTH_POLICY = "Bearer";
+
+ /** Whether the bearer authentication process is complete */
+ private boolean mComplete;
+
+ /** Authentication parameter map */
+ private Map mParams = null;
+
+
+ /**
+ * Default constructor for the bearer authentication scheme.
+ */
+ public BearerAuthScheme() {
+ mComplete = false;
+ }
+
+ /**
+ * Constructor for the basic authentication scheme.
+ *
+ * @param challenge Authentication challenge
+ *
+ * @throws MalformedChallengeException Thrown if the authentication challenge is malformed
+ *
+ * @deprecated Use parameterless constructor and {@link AuthScheme#processChallenge(String)} method
+ */
+ public BearerAuthScheme(final String challenge) throws MalformedChallengeException {
+ processChallenge(challenge);
+ mComplete = true;
+ }
+
+ /**
+ * Returns textual designation of the bearer authentication scheme.
+ *
+ * @return "Bearer"
+ */
+ public String getSchemeName() {
+ return "bearer";
+ }
+
+ /**
+ * Processes the Bearer challenge.
+ *
+ * @param challenge The challenge string
+ *
+ * @throws MalformedChallengeException Thrown if the authentication challenge is malformed
+ */
+ public void processChallenge(String challenge) throws MalformedChallengeException {
+ String s = AuthChallengeParser.extractScheme(challenge);
+ if (!s.equalsIgnoreCase(getSchemeName())) {
+ throw new MalformedChallengeException(
+ "Invalid " + getSchemeName() + " challenge: " + challenge);
+ }
+ mParams = AuthChallengeParser.extractParams(challenge);
+ mComplete = true;
+ }
+
+ /**
+ * Tests if the Bearer authentication process has been completed.
+ *
+ * @return 'true' if Bearer authorization has been processed, 'false' otherwise.
+ */
+ public boolean isComplete() {
+ return this.mComplete;
+ }
+
+ /**
+ * Produces bearer authorization string for the given set of
+ * {@link Credentials}.
+ *
+ * @param credentials The set of credentials to be used for authentication
+ * @param method Method name is ignored by the bearer authentication scheme
+ * @param uri URI is ignored by the bearer authentication scheme
+ * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable
+ * for this authentication scheme
+ * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure
+ * @return A bearer authorization string
+ *
+ * @deprecated Use {@link #authenticate(Credentials, HttpMethod)}
+ */
+ public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException {
+ Log.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)");
+
+ BearerCredentials bearer = null;
+ try {
+ bearer = (BearerCredentials) credentials;
+ } catch (ClassCastException e) {
+ throw new InvalidCredentialsException(
+ "Credentials cannot be used for bearer authentication: "
+ + credentials.getClass().getName());
+ }
+ return BearerAuthScheme.authenticate(bearer);
+ }
+
+
+ /**
+ * Returns 'false'. Bearer authentication scheme is request based.
+ *
+ * @return 'false'.
+ */
+ public boolean isConnectionBased() {
+ return false;
+ }
+
+ /**
+ * Produces bearer authorization string for the given set of {@link Credentials}.
+ *
+ * @param credentials The set of credentials to be used for authentication
+ * @param method The method being authenticated
+ * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable for this authentication
+ * scheme.
+ * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure.
+ *
+ * @return a basic authorization string
+ */
+ public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException {
+ Log.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)");
+
+ if (method == null) {
+ throw new IllegalArgumentException("Method may not be null");
+ }
+ BearerCredentials bearer = null;
+ try {
+ bearer = (BearerCredentials) credentials;
+ } catch (ClassCastException e) {
+ throw new InvalidCredentialsException(
+ "Credentials cannot be used for bearer authentication: "
+ + credentials.getClass().getName());
+ }
+ return BearerAuthScheme.authenticate(
+ bearer,
+ method.getParams().getCredentialCharset());
+ }
+
+ /**
+ * @deprecated Use {@link #authenticate(BearerCredentials, String)}
+ *
+ * Returns a bearer Authorization header value for the given
+ * {@link BearerCredentials}.
+ *
+ * @param credentials The credentials to encode.
+ *
+ * @return A bearer authorization string
+ */
+ public static String authenticate(BearerCredentials credentials) {
+ return authenticate(credentials, "ISO-8859-1");
+ }
+
+ /**
+ * Returns a bearer Authorization header value for the given
+ * {@link BearerCredentials} and charset.
+ *
+ * @param credentials The credentials to encode.
+ * @param charset The charset to use for encoding the credentials
+ *
+ * @return A bearer authorization string
+ *
+ * @since 3.0
+ */
+ public static String authenticate(BearerCredentials credentials, String charset) {
+ Log.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)");
+
+ if (credentials == null) {
+ throw new IllegalArgumentException("Credentials may not be null");
+ }
+ if (charset == null || charset.length() == 0) {
+ throw new IllegalArgumentException("charset may not be null or empty");
+ }
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(credentials.getAccessToken());
+
+ //return "Bearer " + EncodingUtil.getAsciiString(EncodingUtil.getBytes(buffer.toString(), charset));
+ return "Bearer " + buffer.toString();
+ }
+
+ /**
+ * Returns a String identifying the authentication challenge. This is
+ * used, in combination with the host and port to determine if
+ * authorization has already been attempted or not. Schemes which
+ * require multiple requests to complete the authentication should
+ * return a different value for each stage in the request.
+ *
+ * Additionally, the ID should take into account any changes to the
+ * authentication challenge and return a different value when appropriate.
+ * For example when the realm changes in basic authentication it should be
+ * considered a different authentication attempt and a different value should
+ * be returned.
+ *
+ * This method simply returns the realm for the challenge.
+ *
+ * @return String a String identifying the authentication challenge.
+ *
+ * @deprecated no longer used
+ */
+ @Override
+ public String getID() {
+ return getRealm();
+ }
+
+ /**
+ * Returns authentication parameter with the given name, if available.
+ *
+ * @param name The name of the parameter to be returned
+ *
+ * @return The parameter with the given name
+ */
+ @Override
+ public String getParameter(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Parameter name may not be null");
+ }
+ if (mParams == null) {
+ return null;
+ }
+ return (String) mParams.get(name.toLowerCase());
+ }
+
+ /**
+ * Returns authentication realm. The realm may not be null.
+ *
+ * @return The authentication realm
+ */
+ @Override
+ public String getRealm() {
+ return getParameter("realm");
+ }
+
+}
--- /dev/null
+/* 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.network;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.util.LangUtils;
+
+/**
+ * Bearer token {@link Credentials}
+ *
+ * @author David A. Velasco
+ */
+public class BearerCredentials implements Credentials {
+
+
+ private String mAccessToken;
+
+
+ /**
+ * The constructor with the bearer token
+ *
+ * @param token The bearer token
+ */
+ public BearerCredentials(String token) {
+ if (token == null) {
+ throw new IllegalArgumentException("Bearer token may not be null");
+ }
+ mAccessToken = token;
+ }
+
+
+ /**
+ * Returns the access token
+ *
+ * @return The access token
+ */
+ public String getAccessToken() {
+ return mAccessToken;
+ }
+
+
+ /**
+ * Get this object string.
+ *
+ * @return The access token
+ */
+ public String toString() {
+ return mAccessToken;
+ }
+
+ /**
+ * Does a hash of the access token.
+ *
+ * @return The hash code of the access token
+ */
+ public int hashCode() {
+ int hash = LangUtils.HASH_SEED;
+ hash = LangUtils.hashCode(hash, mAccessToken);
+ return hash;
+ }
+
+ /**
+ * These credentials are assumed equal if accessToken is the same.
+ *
+ * @param o The other object to compare with.
+ *
+ * @return 'True' if the object is equivalent.
+ */
+ public boolean equals(Object o) {
+ if (o == null) return false;
+ if (this == o) return true;
+ if (this.getClass().equals(o.getClass())) {
+ BearerCredentials that = (BearerCredentials) o;
+ if (LangUtils.equals(mAccessToken, that.mAccessToken)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
+
import org.apache.http.conn.ssl.X509HostnameVerifier;
import com.owncloud.android.AccountUtils;
+import com.owncloud.android.authenticator.AccountAuthenticator;
import eu.alefzero.webdav.WebdavClient;
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
import android.content.Context;
import android.net.Uri;
+import android.os.Bundle;
import android.util.Log;
public class OwnCloudClientUtils {
- final private static String TAG = "OwnCloudClientFactory";
+ final private static String TAG = OwnCloudClientUtils.class.getSimpleName();
/** Default timeout for waiting data from the server */
public static final int DEFAULT_DATA_TIMEOUT = 60000;
/**
* Creates a WebdavClient setup for an ownCloud account
*
- * @param account The ownCloud account
- * @param context The application context
- * @return A WebdavClient object ready to be used
+ * Do not call this method from the main thread.
+ *
+ * @param account The ownCloud account
+ * @param appContext Android application context
+ * @return A WebdavClient object ready to be used
+ * @throws AuthenticatorException If the authenticator failed to get the authorization token for the account.
+ * @throws OperationCanceledException If the authenticator operation was cancelled while getting the authorization token for the account.
+ * @throws IOException If there was some I/O error while getting the authorization token for the account.
*/
- public static WebdavClient createOwnCloudClient (Account account, Context context) {
- Log.d(TAG, "Creating WebdavClient associated to " + account.name);
+ public static WebdavClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException {
+ //Log.d(TAG, "Creating WebdavClient associated to " + account.name);
- Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(context, account));
- WebdavClient client = createOwnCloudClient(uri, context);
+ Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
+ WebdavClient client = createOwnCloudClient(uri, appContext);
+ AccountManager am = AccountManager.get(appContext);
+ if (am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null) { // TODO avoid a call to getUserData here
+ String accessToken = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, false);
+ client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token
- 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);
-
- client.setCredentials(username, password);
+ } else {
+ String username = account.name.substring(0, account.name.lastIndexOf('@'));
+ //String password = am.getPassword(account);
+ String password = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_PASSWORD, false);
+ client.setBasicCredentials(username, password);
+ }
return client;
}
+ public static WebdavClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException {
+ Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
+ WebdavClient client = createOwnCloudClient(uri, appContext);
+ AccountManager am = AccountManager.get(appContext);
+ if (am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null) { // TODO avoid a call to getUserData here
+ AccountManagerFuture<Bundle> future = am.getAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, null, currentActivity, null, null);
+ Bundle result = future.getResult();
+ String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
+ //String accessToken = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, false);
+ if (accessToken == null) throw new AuthenticatorException("WTF!");
+ client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token
+
+ } else {
+ String username = account.name.substring(0, account.name.lastIndexOf('@'));
+ //String password = am.getPassword(account);
+ //String password = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_PASSWORD, false);
+ AccountManagerFuture<Bundle> future = am.getAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_PASSWORD, null, currentActivity, null, null);
+ Bundle result = future.getResult();
+ String password = result.getString(AccountManager.KEY_AUTHTOKEN);
+ client.setBasicCredentials(username, password);
+ }
+
+ return client;
+ }
+
/**
* Creates a WebdavClient to try a new account before saving it
*
* @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);
- client.setCredentials(username, password);
+ client.setBasicCredentials(username, password);
return client;
}
* @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 {
return mConnManager;
}
+
}
--- /dev/null
+/* 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.jackrabbit.webdav.client.methods.MkColMethod;
+
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+
+import android.util.Log;
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Remote operation performing the creation of a new folder in the ownCloud server.
+ *
+ * @author David A. Velasco
+ */
+public class CreateFolderOperation extends RemoteOperation {
+
+ private static final String TAG = CreateFolderOperation.class.getSimpleName();
+
+ private static final int READ_TIMEOUT = 10000;
+ private static final int CONNECTION_TIMEOUT = 5000;
+
+ protected String mRemotePath;
+ protected long mParentDirId;
+ protected DataStorageManager mStorageManager;
+
+ /**
+ * Constructor
+ *
+ * @param remoetPath Full path to the new directory to create in the remote server.
+ * @param parentDirId Local database id for the parent folder.
+ * @param storageManager Reference to the local database corresponding to the account where the file is contained.
+ */
+ public CreateFolderOperation(String remotePath, long parentDirId, DataStorageManager storageManager) {
+ mRemotePath = remotePath;
+ mParentDirId = parentDirId;
+ mStorageManager = storageManager;
+ }
+
+
+ /**
+ * Performs the remove operation
+ *
+ * @param client Client object to communicate with the remote ownCloud server.
+ */
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ RemoteOperationResult result = null;
+ MkColMethod mkcol = null;
+ try {
+ mkcol = new MkColMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
+ int status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT);
+ if (mkcol.succeeded()) {
+ // Save new directory in local database
+ OCFile newDir = new OCFile(mRemotePath);
+ newDir.setMimetype("DIR");
+ newDir.setParentId(mParentDirId);
+ mStorageManager.saveFile(newDir);
+ }
+
+ result = new RemoteOperationResult(mkcol.succeeded(), status);
+ Log.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage());
+ client.exhaustResponse(mkcol.getResponseBodyAsStream());
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+ Log.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e);
+
+ } finally {
+ if (mkcol != null)
+ mkcol.releaseConnection();
+ }
+ return result;
+ }
+
+}
--- /dev/null
+/* 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();
+ }
+
+
+ public String getAccessToken() {
+ return mAccessToken;
+ }
+
+}
--- /dev/null
+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));
+ }
+ }
+
+}
*/
package com.owncloud.android.operations;
+import java.io.IOException;
+
+import com.owncloud.android.network.OwnCloudClientUtils;
+
+import android.accounts.Account;
+import android.accounts.AccountsException;
+import android.app.Activity;
+import android.content.Context;
import android.os.Handler;
+import android.util.Log;
import eu.alefzero.webdav.WebdavClient;
*/
public abstract class RemoteOperation implements Runnable {
- /** Object to interact with the ownCloud server */
+ private static final String TAG = RemoteOperation.class.getSimpleName();
+
+ /** ownCloud account in the remote ownCloud server to operate */
+ private Account mAccount = null;
+
+ /** Android Application context */
+ private Context mContext = null;
+
+ /** Object to interact with the remote server */
private WebdavClient mClient = null;
/** Callback object to notify about the execution of the remote operation */
/** Handler to the thread where mListener methods will be called */
private Handler mListenerHandler = null;
+ /** Activity */
+ private Activity mCallerActivity;
+
/**
* Abstract method to implement the operation in derived classes.
*/
protected abstract RemoteOperationResult run(WebdavClient client);
+
+ /**
+ * Synchronously executes the remote operation on the received ownCloud account.
+ *
+ * Do not call this method from the main thread.
+ *
+ * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
+ *
+ * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
+ * @param context Android context for the component calling the method.
+ * @return Result of the operation.
+ */
+ public final RemoteOperationResult execute(Account account, Context context) {
+ if (account == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
+ if (context == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
+ mAccount = account;
+ mContext = context.getApplicationContext();
+ try {
+ mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
+ } catch (Exception e) {
+ Log.e(TAG, "Error while trying to access to " + mAccount.name, e);
+ return new RemoteOperationResult(e);
+ }
+ return run(mClient);
+ }
+
/**
* Synchronously executes the remote operation
*
+ * Do not call this method from the main thread.
+ *
* @param client Client object to reach an ownCloud server during the execution of the operation.
* @return Result of the operation.
*/
}
+ /**
+ * Asynchronously executes the remote operation
+ *
+ * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
+ *
+ * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
+ * @param context Android context for the component calling the method.
+ * @param listener Listener to be notified about the execution of the operation.
+ * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
+ * @return Thread were the remote operation is executed.
+ */
+ public final Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) {
+ if (account == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
+ if (context == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
+ mAccount = account;
+ mContext = context.getApplicationContext();
+ mCallerActivity = callerActivity;
+ mClient = null; // the client instance will be created from mAccount and mContext in the runnerThread to create below
+
+ if (listener == null) {
+ throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
+ }
+ mListener = listener;
+
+ if (listenerHandler == null) {
+ throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
+ }
+ mListenerHandler = listenerHandler;
+
+ Thread runnerThread = new Thread(this);
+ runnerThread.start();
+ return runnerThread;
+ }
+
+
/**
* Asynchronously executes the remote operation
*
*/
@Override
public final void run() {
- final RemoteOperationResult result = execute(mClient);
+ RemoteOperationResult result = null;
+ try{
+ if (mClient == null) {
+ if (mAccount != null && mContext != null) {
+ if (mCallerActivity != null) {
+ mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext, mCallerActivity);
+ } else {
+ mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
+ }
+ } else {
+ throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account");
+ }
+ }
+
+ } catch (IOException e) {
+ Log.e(TAG, "Error while trying to access to " + mAccount.name, new AccountsException("I/O exception while trying to authorize the account", e));
+ result = new RemoteOperationResult(e);
+
+ } catch (AccountsException e) {
+ Log.e(TAG, "Error while trying to access to " + mAccount.name, e);
+ result = new RemoteOperationResult(e);
+ }
+ if (result == null)
+ result = run(mClient);
+
+ final RemoteOperationResult resultToSend = result;
if (mListenerHandler != null && mListener != null) {
mListenerHandler.post(new Runnable() {
@Override
public void run() {
- mListener.onRemoteOperationFinish(RemoteOperation.this, result);
+ mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
}
});
}
}
-
-
+
+
+ /**
+ * Returns the current client instance to access the remote server.
+ *
+ * @return Current client instance to access the remote server.
+ */
+ public final WebdavClient getClient() {
+ return mClient;
+ }
+
}
/** Generated - should be refreshed every time the class changes!! */
private static final long serialVersionUID = -7805531062432602444L;
-
public enum ResultCode {
OK,
INVALID_LOCAL_FILE_NAME,
INVALID_OVERWRITE,
CONFLICT,
+ OAUTH2_ERROR,
SYNC_CONFLICT,
LOCAL_STORAGE_FULL,
LOCAL_STORAGE_NOT_MOVED,
*/
public class RenameFileOperation extends RemoteOperation {
- private static final String TAG = RemoveFileOperation.class.getSimpleName();
+ private static final String TAG = RenameFileOperation.class.getSimpleName();
private static final int RENAME_READ_TIMEOUT = 10000;
private static final int RENAME_CONNECTION_TIMEOUT = 5000;
return null;\r
}\r
\r
- protected void initClientForCurrentAccount() throws UnknownHostException {\r
+ protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException {\r
if (AccountUtils.constructFullURLForAccount(getContext(), account) == null) {\r
throw new UnknownHostException();\r
}\r
import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;\r
import android.accounts.Account;\r
+import android.accounts.AccountsException;\r
+import android.accounts.AuthenticatorException;\r
+import android.accounts.OperationCanceledException;\r
import android.app.Notification;\r
import android.app.NotificationManager;\r
import android.app.PendingIntent;\r
this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));\r
try {\r
this.initClientForCurrentAccount();\r
- } catch (UnknownHostException e) {\r
+ } catch (IOException e) {\r
+ /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again\r
+ mSyncResult.tooManyRetries = true;\r
+ notifyFailedSynchronization();\r
+ return;\r
+ } catch (AccountsException e) {\r
/// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again\r
mSyncResult.tooManyRetries = true;\r
notifyFailedSynchronization();\r
\r
import java.net.MalformedURLException;\r
import java.net.URL;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import org.json.JSONException;\r
+import org.json.JSONObject;\r
\r
import com.owncloud.android.AccountUtils;\r
import com.owncloud.android.authenticator.AccountAuthenticator;\r
import com.owncloud.android.authenticator.AuthenticationRunnable;\r
import com.owncloud.android.authenticator.OnAuthenticationResultListener;\r
import com.owncloud.android.authenticator.OnConnectCheckListener;\r
+import com.owncloud.android.authenticator.oauth2.OAuth2Context;\r
+import com.owncloud.android.authenticator.oauth2.OAuth2GetCodeRunnable;\r
+import com.owncloud.android.authenticator.oauth2.OnOAuth2GetCodeResultListener;\r
+import com.owncloud.android.authenticator.oauth2.connection.ConnectorOAuth2;\r
+import com.owncloud.android.authenticator.oauth2.services.OAuth2GetTokenService;\r
import com.owncloud.android.ui.dialog.SslValidatorDialog;\r
import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;\r
+import com.owncloud.android.utils.OwnCloudVersion;\r
import com.owncloud.android.network.OwnCloudClientUtils;\r
import com.owncloud.android.operations.ConnectionCheckOperation;\r
+import com.owncloud.android.operations.ExistenceCheckOperation;\r
+import com.owncloud.android.operations.GetOAuth2AccessToken;\r
import com.owncloud.android.operations.OnRemoteOperationListener;\r
import com.owncloud.android.operations.RemoteOperation;\r
import com.owncloud.android.operations.RemoteOperationResult;\r
import android.app.AlertDialog;\r
import android.app.Dialog;\r
import android.app.ProgressDialog;\r
+import android.content.BroadcastReceiver;\r
import android.content.ContentResolver;\r
+import android.content.Context;\r
import android.content.DialogInterface;\r
import android.content.Intent;\r
+import android.content.IntentFilter;\r
import android.content.SharedPreferences;\r
import android.net.Uri;\r
import android.os.Bundle;\r
import android.view.View.OnClickListener;\r
import android.view.View.OnFocusChangeListener;\r
import android.view.Window;\r
-import android.widget.Button;\r
+import android.widget.CheckBox;\r
import android.widget.EditText;\r
+import android.widget.Button;\r
import android.widget.ImageView;\r
import android.widget.TextView;\r
import com.owncloud.android.R;\r
*/\r
public class AuthenticatorActivity extends AccountAuthenticatorActivity\r
implements OnAuthenticationResultListener, OnConnectCheckListener, OnRemoteOperationListener, OnSslValidatorListener, \r
- OnFocusChangeListener, OnClickListener {\r
+ OnFocusChangeListener, OnClickListener, OnOAuth2GetCodeResultListener {\r
\r
private static final int DIALOG_LOGIN_PROGRESS = 0;\r
private static final int DIALOG_SSL_VALIDATOR = 1;\r
\r
private Thread mAuthThread;\r
private AuthenticationRunnable mAuthRunnable;\r
- //private ConnectionCheckerRunnable mConnChkRunnable = null;\r
private ConnectionCheckOperation mConnChkRunnable;\r
+ private ExistenceCheckOperation mAuthChkOperation;\r
private final Handler mHandler = new Handler();\r
private String mBaseUrl;\r
+ private OwnCloudVersion mDiscoveredVersion;\r
\r
private static final String STATUS_TEXT = "STATUS_TEXT";\r
private static final String STATUS_ICON = "STATUS_ICON";\r
private static final String STATUS_CORRECT = "STATUS_CORRECT";\r
private static final String IS_SSL_CONN = "IS_SSL_CONN";\r
+ private static final String OC_VERSION = "OC_VERSION";\r
private int mStatusText, mStatusIcon;\r
private boolean mStatusCorrect, mIsSslConn;\r
private RemoteOperationResult mLastSslUntrustedServerResult;\r
\r
+ public static final String PARAM_ACCOUNTNAME = "param_Accountname";\r
+ \r
public static final String PARAM_USERNAME = "param_Username";\r
public static final String PARAM_HOSTNAME = "param_Hostname";\r
\r
+ // oAuth2 variables.\r
+ private static final int OAUTH2_LOGIN_PROGRESS = 3;\r
+ private static final String OAUTH2_STATUS_TEXT = "OAUTH2_STATUS_TEXT";\r
+ private static final String OAUTH2_STATUS_ICON = "OAUTH2_STATUS_ICON";\r
+ private static final String OAUTH2_CODE_RESULT = "CODE_RESULT";\r
+ private static final String OAUTH2_IS_CHECKED = "OAUTH2_IS_CHECKED"; \r
+ private Thread mOAuth2GetCodeThread;\r
+ private OAuth2GetCodeRunnable mOAuth2GetCodeRunnable; \r
+ private TokenReceiver tokenReceiver;\r
+ private JSONObject codeResponseJson; \r
+ private int mOAuth2StatusText, mOAuth2StatusIcon; \r
+ \r
+ public ConnectorOAuth2 connectorOAuth2;\r
+ \r
+ // Variables used to save the on the state the contents of all fields.\r
+ private static final String HOST_URL_TEXT = "HOST_URL_TEXT";\r
+ private static final String ACCOUNT_USERNAME = "ACCOUNT_USERNAME";\r
+ private static final String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD";\r
+ \r
+ //private boolean mNewRedirectUriCaptured;\r
+ private Uri mNewCapturedUriFromOAuth2Redirection;\r
+\r
+ // END of oAuth2 variables.\r
+ \r
@Override\r
protected void onCreate(Bundle savedInstanceState) {\r
super.onCreate(savedInstanceState);\r
ImageView iv2 = (ImageView) findViewById(R.id.viewPassword);\r
TextView tv = (TextView) findViewById(R.id.host_URL);\r
TextView tv2 = (TextView) findViewById(R.id.account_password);\r
+ EditText oauth2Url = (EditText)findViewById(R.id.oAuth_URL);\r
+ oauth2Url.setText("OWNCLOUD AUTHORIZATION PROVIDER IN TEST");\r
\r
if (savedInstanceState != null) {\r
mStatusIcon = savedInstanceState.getInt(STATUS_ICON);\r
if (!mStatusCorrect)\r
iv.setVisibility(View.VISIBLE);\r
else\r
- iv.setVisibility(View.INVISIBLE);\r
+ iv.setVisibility(View.INVISIBLE); \r
+ \r
+ String ocVersion = savedInstanceState.getString(OC_VERSION, null);\r
+ if (ocVersion != null)\r
+ mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
+ \r
+ // Getting the state of oAuth2 components.\r
+ mOAuth2StatusIcon = savedInstanceState.getInt(OAUTH2_STATUS_ICON);\r
+ mOAuth2StatusText = savedInstanceState.getInt(OAUTH2_STATUS_TEXT);\r
+ // We set this to true if the rotation happens when the user is validating oAuth2 user_code.\r
+ changeViewByOAuth2Check(savedInstanceState.getBoolean(OAUTH2_IS_CHECKED));\r
+ // We store a JSon object with all the data returned from oAuth2 server when we get user_code.\r
+ // Is better than store variable by variable. We use String object to serialize from/to it.\r
+ try {\r
+ if (savedInstanceState.containsKey(OAUTH2_CODE_RESULT)) {\r
+ codeResponseJson = new JSONObject(savedInstanceState.getString(OAUTH2_CODE_RESULT));\r
+ }\r
+ } catch (JSONException e) {\r
+ Log.e(TAG, "onCreate->JSONException: " + e.toString());\r
+ }\r
+ // END of getting the state of oAuth2 components.\r
+ \r
+ // Getting contents of each field.\r
+ EditText hostUrl = (EditText)findViewById(R.id.host_URL);\r
+ hostUrl.setText(savedInstanceState.getString(HOST_URL_TEXT), TextView.BufferType.EDITABLE);\r
+ EditText accountUsername = (EditText)findViewById(R.id.account_username);\r
+ accountUsername.setText(savedInstanceState.getString(ACCOUNT_USERNAME), TextView.BufferType.EDITABLE);\r
+ EditText accountPassword = (EditText)findViewById(R.id.account_password);\r
+ accountPassword.setText(savedInstanceState.getString(ACCOUNT_PASSWORD), TextView.BufferType.EDITABLE);\r
+ // END of getting contents of each field\r
\r
} else {\r
mStatusText = mStatusIcon = 0;\r
mStatusCorrect = false;\r
mIsSslConn = false;\r
+ \r
+ String accountName = getIntent().getExtras().getString(PARAM_ACCOUNTNAME);\r
+ String tokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
+ if (AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN.equals(tokenType)) {\r
+ CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
+ oAuth2Check.setChecked(true);\r
+ changeViewByOAuth2Check(true);\r
+ } \r
+ \r
+ if (accountName != null) {\r
+ ((TextView) findViewById(R.id.account_username)).setText(accountName.substring(0, accountName.lastIndexOf('@')));\r
+ tv.setText(accountName.substring(accountName.lastIndexOf('@') + 1));\r
+ }\r
}\r
iv.setOnClickListener(this);\r
iv2.setOnClickListener(this);\r
tv.setOnFocusChangeListener(this);\r
tv2.setOnFocusChangeListener(this);\r
-\r
+ \r
Button b = (Button) findViewById(R.id.account_register);\r
if (b != null) {\r
b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name)));\r
}\r
+\r
+ mNewCapturedUriFromOAuth2Redirection = null;\r
}\r
\r
+ \r
+ @Override\r
+ protected void onNewIntent (Intent intent) {\r
+ Uri data = intent.getData();\r
+ //mNewRedirectUriCaptured = (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI));\r
+ if (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI)) {\r
+ mNewCapturedUriFromOAuth2Redirection = data;\r
+ }\r
+ Log.d(TAG, "onNewIntent()");\r
+ \r
+ }\r
+ \r
+ \r
@Override\r
protected void onSaveInstanceState(Bundle outState) {\r
outState.putInt(STATUS_ICON, mStatusIcon);\r
outState.putInt(STATUS_TEXT, mStatusText);\r
outState.putBoolean(STATUS_CORRECT, mStatusCorrect);\r
+ if (mDiscoveredVersion != null) \r
+ outState.putString(OC_VERSION, mDiscoveredVersion.toString());\r
+ \r
+ // Saving the state of oAuth2 components.\r
+ outState.putInt(OAUTH2_STATUS_ICON, mOAuth2StatusIcon);\r
+ outState.putInt(OAUTH2_STATUS_TEXT, mOAuth2StatusText);\r
+ CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
+ outState.putBoolean(OAUTH2_IS_CHECKED, oAuth2Check.isChecked());\r
+ if (codeResponseJson != null){\r
+ outState.putString(OAUTH2_CODE_RESULT, codeResponseJson.toString());\r
+ }\r
+ // END of saving the state of oAuth2 components.\r
+ \r
+ // Saving contents of each field.\r
+ outState.putString(HOST_URL_TEXT,((TextView) findViewById(R.id.host_URL)).getText().toString().trim());\r
+ outState.putString(ACCOUNT_USERNAME,((TextView) findViewById(R.id.account_username)).getText().toString().trim());\r
+ outState.putString(ACCOUNT_PASSWORD,((TextView) findViewById(R.id.account_password)).getText().toString().trim());\r
+ \r
super.onSaveInstanceState(outState);\r
}\r
\r
dialog = working_dialog;\r
break;\r
}\r
+ // oAuth2 dialog. We show here to the user the URL and user_code that the user must validate in a web browser.\r
+ case OAUTH2_LOGIN_PROGRESS: {\r
+ ProgressDialog working_dialog = new ProgressDialog(this);\r
+ try {\r
+ if (codeResponseJson != null && codeResponseJson.has(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL)) {\r
+ working_dialog.setMessage(String.format(getString(R.string.oauth_code_validation_message), \r
+ codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL), \r
+ codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE)));\r
+ } else {\r
+ working_dialog.setMessage(String.format("Getting authorization"));\r
+ }\r
+ } catch (JSONException e) {\r
+ Log.e(TAG, "onCreateDialog->JSONException: " + e.toString());\r
+ }\r
+ working_dialog.setIndeterminate(true);\r
+ working_dialog.setCancelable(true);\r
+ working_dialog\r
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
+ @Override\r
+ public void onCancel(DialogInterface dialog) {\r
+ Log.i(TAG, "Login canceled");\r
+ if (mOAuth2GetCodeThread != null) {\r
+ mOAuth2GetCodeThread.interrupt();\r
+ finish();\r
+ } \r
+ if (tokenReceiver != null) {\r
+ unregisterReceiver(tokenReceiver);\r
+ tokenReceiver = null;\r
+ finish();\r
+ }\r
+ }\r
+ });\r
+ dialog = working_dialog;\r
+ break;\r
+ }\r
case DIALOG_SSL_VALIDATOR: {\r
dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);\r
break;\r
switch (id) {\r
case DIALOG_LOGIN_PROGRESS:\r
case DIALOG_CERT_NOT_SAVED:\r
+ case OAUTH2_LOGIN_PROGRESS:\r
break;\r
case DIALOG_SSL_VALIDATOR: {\r
((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);\r
Log.e(TAG, "Incorrect dialog called with id = " + id);\r
}\r
}\r
+ \r
+ @Override\r
+ protected void onResume() {\r
+ Log.d(TAG, "onResume() start");\r
+ // (old oauth code) Registering token receiver. We must listening to the service that is pooling to the oAuth server for a token.\r
+ if (tokenReceiver == null) {\r
+ IntentFilter tokenFilter = new IntentFilter(OAuth2GetTokenService.TOKEN_RECEIVED_MESSAGE); \r
+ tokenReceiver = new TokenReceiver();\r
+ this.registerReceiver(tokenReceiver,tokenFilter);\r
+ }\r
+ // (new oauth code)\r
+ /*if (mNewRedirectUriCaptured) {\r
+ mNewRedirectUriCaptured = false;*/\r
+ if (mNewCapturedUriFromOAuth2Redirection != null) {\r
+ getOAuth2AccessTokenFromCapturedRedirection(); \r
+ \r
+ }\r
+ super.onResume();\r
+ }\r
+\r
+ @Override\r
+ protected void onPause() {\r
+ Log.d(TAG, "onPause() start");\r
+ super.onPause();\r
+ } \r
+ \r
\r
public void onAuthenticationResult(boolean success, String message) {\r
if (success) {\r
AccountAuthenticator.ACCOUNT_TYPE);\r
intent.putExtra(AccountManager.KEY_USERDATA, username);\r
\r
+ accManager.setUserData(account, AccountAuthenticator.KEY_OC_URL,\r
+ url.toString());\r
accManager.setUserData(account,\r
- AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable\r
- .getDiscoveredVersion().toString());\r
+ AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString());\r
\r
accManager.setUserData(account,\r
AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);\r
|| url.toLowerCase().startsWith("https://")) {\r
prefix = "";\r
}\r
- continueConnection(prefix);\r
+ CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
+ if (oAuth2Check != null && oAuth2Check.isChecked()) {\r
+ startOauthorization();\r
+ \r
+ } else {\r
+ continueConnection(prefix);\r
+ }\r
}\r
\r
+ private void startOauthorization() {\r
+ // We start a thread to get an authorization code from the oAuth2 server.\r
+ setOAuth2ResultIconAndText(R.drawable.progress_small, R.string.oauth_login_connection);\r
+ mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(OAuth2Context.OAUTH2_F_AUTHORIZATION_ENDPOINT_URL, this);\r
+ //mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(OAuth2Context.OAUTH2_G_DEVICE_GETCODE_URL, this);\r
+ mOAuth2GetCodeRunnable.setListener(this, mHandler);\r
+ mOAuth2GetCodeThread = new Thread(mOAuth2GetCodeRunnable);\r
+ mOAuth2GetCodeThread.start();\r
+ }\r
+\r
public void onRegisterClick(View view) {\r
Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register)));\r
setResult(RESULT_CANCELED);\r
url = url.substring(0, url.length() - 1);\r
\r
URL uri = null;\r
- String webdav_path = AccountUtils.getWebdavPath(mConnChkRunnable\r
- .getDiscoveredVersion());\r
+ mDiscoveredVersion = mConnChkRunnable.getDiscoveredVersion();\r
+ String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, false);\r
\r
if (webdav_path == null) {\r
onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title));\r
findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);\r
}\r
\r
- @Override\r
public void onFocusChange(View view, boolean hasFocus) {\r
if (view.getId() == R.id.host_URL) {\r
if (!hasFocus) {\r
setResultIconAndText(R.drawable.progress_small,\r
R.string.auth_testing_connection);\r
//mConnChkRunnable = new ConnectionCheckerRunnable(uri, this);\r
- mConnChkRunnable = new ConnectionCheckOperation(uri, this);\r
+ mConnChkRunnable = new ConnectionCheckOperation(uri, this);\r
//mConnChkRunnable.setListener(this, mHandler);\r
//mAuthThread = new Thread(mConnChkRunnable);\r
//mAuthThread.start();\r
WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this);\r
+ mDiscoveredVersion = null;\r
mAuthThread = mConnChkRunnable.execute(client, this, mHandler);\r
} else {\r
findViewById(R.id.refreshButton).setVisibility(\r
view.setSelection(selectionStart, selectionEnd);\r
}\r
}\r
+ \r
+ @Override protected void onDestroy() { \r
+ // We must stop the service thats it's pooling to oAuth2 server for a token.\r
+ Intent tokenService = new Intent(this, OAuth2GetTokenService.class);\r
+ stopService(tokenService);\r
+ \r
+ // We stop listening the result of the pooling service.\r
+ if (tokenReceiver != null) {\r
+ unregisterReceiver(tokenReceiver);\r
+ tokenReceiver = null;\r
+ finish();\r
+ }\r
+\r
+ super.onDestroy();\r
+ } \r
+ \r
+ // Controlling the oAuth2 checkbox on the activity: hide and show widgets.\r
+ public void onOff_check_Click(View view) {\r
+ CheckBox oAuth2Check = (CheckBox)view; \r
+ changeViewByOAuth2Check(oAuth2Check.isChecked());\r
+\r
+ }\r
+ \r
+ public void changeViewByOAuth2Check(Boolean checked) {\r
+ \r
+ EditText oAuth2Url = (EditText) findViewById(R.id.oAuth_URL);\r
+ EditText accountUsername = (EditText) findViewById(R.id.account_username);\r
+ EditText accountPassword = (EditText) findViewById(R.id.account_password);\r
+ ImageView viewPassword = (ImageView) findViewById(R.id.viewPassword); \r
+ ImageView auth2ActionIndicator = (ImageView) findViewById(R.id.auth2_action_indicator); \r
+ TextView oauth2StatusText = (TextView) findViewById(R.id.oauth2_status_text); \r
+\r
+ if (checked) {\r
+ oAuth2Url.setVisibility(View.VISIBLE);\r
+ accountUsername.setVisibility(View.GONE);\r
+ accountPassword.setVisibility(View.GONE);\r
+ viewPassword.setVisibility(View.GONE);\r
+ auth2ActionIndicator.setVisibility(View.INVISIBLE);\r
+ oauth2StatusText.setVisibility(View.INVISIBLE);\r
+ } else {\r
+ oAuth2Url.setVisibility(View.GONE);\r
+ accountUsername.setVisibility(View.VISIBLE);\r
+ accountPassword.setVisibility(View.VISIBLE);\r
+ viewPassword.setVisibility(View.INVISIBLE);\r
+ auth2ActionIndicator.setVisibility(View.GONE);\r
+ oauth2StatusText.setVisibility(View.GONE);\r
+ } \r
+\r
+ } \r
+ \r
+ // Controlling the oAuth2 result of server connection.\r
+ private void setOAuth2ResultIconAndText(int drawable_id, int text_id) {\r
+ ImageView iv = (ImageView) findViewById(R.id.auth2_action_indicator);\r
+ TextView tv = (TextView) findViewById(R.id.oauth2_status_text);\r
+\r
+ if (drawable_id == 0 && text_id == 0) {\r
+ iv.setVisibility(View.INVISIBLE);\r
+ tv.setVisibility(View.INVISIBLE);\r
+ } else {\r
+ iv.setImageResource(drawable_id);\r
+ tv.setText(text_id);\r
+ iv.setVisibility(View.VISIBLE);\r
+ tv.setVisibility(View.VISIBLE);\r
+ }\r
+ } \r
+ \r
+ // Results from the first call to oAuth2 server : getting the user_code and verification_url.\r
+ @Override\r
+ public void onOAuth2GetCodeResult(ResultOAuthType type, JSONObject responseJson) {\r
+ if ((type == ResultOAuthType.OK_SSL)||(type == ResultOAuthType.OK_NO_SSL)) {\r
+ codeResponseJson = responseJson;\r
+ if (codeResponseJson != null) {\r
+ getOAuth2AccessTokenFromJsonResponse();\r
+ } // else - nothing to do here - wait for callback !!!\r
+ \r
+ } else if (type == ResultOAuthType.HOST_NOT_AVAILABLE) {\r
+ setOAuth2ResultIconAndText(R.drawable.common_error, R.string.oauth_connection_url_unavailable);\r
+ }\r
+ }\r
+\r
+ // If the results of getting the user_code and verification_url are OK, we get the received data and we start\r
+ // the polling service to oAuth2 server to get a valid token.\r
+ private void getOAuth2AccessTokenFromJsonResponse() {\r
+ String deviceCode = null;\r
+ String verificationUrl = null;\r
+ String userCode = null;\r
+ int expiresIn = -1;\r
+ int interval = -1;\r
+\r
+ Log.d(TAG, "ResponseOAuth2->" + codeResponseJson.toString());\r
+\r
+ try {\r
+ // We get data that we must show to the user or we will use internally.\r
+ verificationUrl = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL);\r
+ userCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE);\r
+ expiresIn = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_EXPIRES_IN); \r
+\r
+ // And we get data that we must use to get a token.\r
+ deviceCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_DEVICE_CODE);\r
+ interval = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_INTERVAL);\r
+\r
+ } catch (JSONException e) {\r
+ Log.e(TAG, "Exception accesing data in Json object" + e.toString());\r
+ }\r
+\r
+ // Updating status widget to OK.\r
+ setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established);\r
+ \r
+ // Showing the dialog with instructions for the user.\r
+ showDialog(OAUTH2_LOGIN_PROGRESS);\r
+\r
+ // Loggin all the data.\r
+ Log.d(TAG, "verificationUrl->" + verificationUrl);\r
+ Log.d(TAG, "userCode->" + userCode);\r
+ Log.d(TAG, "deviceCode->" + deviceCode);\r
+ Log.d(TAG, "expiresIn->" + expiresIn);\r
+ Log.d(TAG, "interval->" + interval);\r
+\r
+ // Starting the pooling service.\r
+ try {\r
+ Intent tokenService = new Intent(this, OAuth2GetTokenService.class);\r
+ tokenService.putExtra(OAuth2GetTokenService.TOKEN_URI, OAuth2Context.OAUTH2_G_DEVICE_GETTOKEN_URL);\r
+ tokenService.putExtra(OAuth2GetTokenService.TOKEN_DEVICE_CODE, deviceCode);\r
+ tokenService.putExtra(OAuth2GetTokenService.TOKEN_INTERVAL, interval);\r
+\r
+ startService(tokenService);\r
+ }\r
+ catch (Exception e) {\r
+ Log.e(TAG, "tokenService creation problem :", e);\r
+ }\r
+ \r
+ } \r
+ \r
+ private void getOAuth2AccessTokenFromCapturedRedirection() {\r
+ Map<String, String> responseValues = new HashMap<String, String>();\r
+ //String queryParameters = getIntent().getData().getQuery();\r
+ String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();\r
+ mNewCapturedUriFromOAuth2Redirection = null;\r
+ \r
+ Log.v(TAG, "Queryparameters (Code) = " + queryParameters);\r
+\r
+ String[] pairs = queryParameters.split("&");\r
+ Log.v(TAG, "Pairs (Code) = " + pairs.toString());\r
+\r
+ int i = 0;\r
+ String key = "";\r
+ String value = "";\r
+\r
+ StringBuilder sb = new StringBuilder();\r
+\r
+ while (pairs.length > i) {\r
+ int j = 0;\r
+ String[] part = pairs[i].split("=");\r
+\r
+ while (part.length > j) {\r
+ String p = part[j];\r
+ if (j == 0) {\r
+ key = p;\r
+ sb.append(key + " = ");\r
+ } else if (j == 1) {\r
+ value = p;\r
+ responseValues.put(key, value);\r
+ sb.append(value + "\n");\r
+ }\r
+\r
+ Log.v(TAG, "[" + i + "," + j + "] = " + p);\r
+ j++;\r
+ }\r
+ i++;\r
+ }\r
+ \r
+ \r
+ // Updating status widget to OK.\r
+ setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established);\r
+ \r
+ // Showing the dialog with instructions for the user.\r
+ showDialog(OAUTH2_LOGIN_PROGRESS);\r
+\r
+ // \r
+ RemoteOperation operation = new GetOAuth2AccessToken(responseValues);\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(OAuth2Context.OAUTH2_F_TOKEN_ENDPOINT_URL), getApplicationContext());\r
+ operation.execute(client, this, mHandler);\r
+ }\r
+\r
+ \r
+\r
+ // We get data from the oAuth2 token service with this broadcast receiver.\r
+ private class TokenReceiver extends BroadcastReceiver {\r
+ /**\r
+ * The token is received.\r
+ * @author\r
+ * {@link BroadcastReceiver} to enable oAuth2 token receiving.\r
+ */\r
+ @Override\r
+ public void onReceive(Context context, Intent intent) {\r
+ @SuppressWarnings("unchecked")\r
+ HashMap<String, String> tokenResponse = (HashMap<String, String>)intent.getExtras().get(OAuth2GetTokenService.TOKEN_RECEIVED_DATA);\r
+ Log.d(TAG, "TokenReceiver->" + tokenResponse.get(OAuth2GetTokenService.TOKEN_ACCESS_TOKEN));\r
+ dismissDialog(OAUTH2_LOGIN_PROGRESS);\r
+\r
+ }\r
+ }\r
\r
@Override\r
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
- if (operation.equals(mConnChkRunnable)) {\r
+ if (operation instanceof ConnectionCheckOperation) {\r
\r
mStatusText = mStatusIcon = 0;\r
mStatusCorrect = false;\r
else\r
findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);\r
findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);\r
+ \r
+ } else if (operation instanceof GetOAuth2AccessToken) {\r
+\r
+ try {\r
+ dismissDialog(OAUTH2_LOGIN_PROGRESS);\r
+ } catch (IllegalArgumentException e) {\r
+ // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
+ }\r
+\r
+ if (result.isSuccess()) {\r
+ \r
+ /// time to test the retrieved access token on the ownCloud server\r
+ String url = ((TextView) findViewById(R.id.host_URL)).getText()\r
+ .toString().trim();\r
+ if (url.endsWith("/"))\r
+ url = url.substring(0, url.length() - 1);\r
+\r
+ Uri uri = null;\r
+ /*String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion);\r
+ \r
+ if (webdav_path == null) {\r
+ onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title));\r
+ return;\r
+ }*/\r
+ \r
+ String prefix = "";\r
+ if (mIsSslConn) {\r
+ prefix = "https://";\r
+ } else {\r
+ prefix = "http://";\r
+ }\r
+ if (url.toLowerCase().startsWith("http://")\r
+ || url.toLowerCase().startsWith("https://")) {\r
+ prefix = "";\r
+ }\r
+ \r
+ try {\r
+ mBaseUrl = prefix + url;\r
+ //String url_str = prefix + url + webdav_path;\r
+ String url_str = prefix + url + "/remote.php/odav";\r
+ uri = Uri.parse(url_str);\r
+ \r
+ } catch (Exception e) {\r
+ // should never happen\r
+ onAuthenticationResult(false, getString(R.string.auth_incorrect_address_title));\r
+ return;\r
+ }\r
+\r
+ showDialog(DIALOG_LOGIN_PROGRESS);\r
+ String accessToken = ((GetOAuth2AccessToken)operation).getResultTokenMap().get(OAuth2Context.KEY_ACCESS_TOKEN);\r
+ Log.d(TAG, "Got ACCESS TOKEN: " + accessToken);\r
+ mAuthChkOperation = new ExistenceCheckOperation("", this, accessToken);\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(uri, getApplicationContext());\r
+ mAuthChkOperation.execute(client, this, mHandler);\r
+ \r
+ \r
+ } else {\r
+ TextView tv = (TextView) findViewById(R.id.oAuth_URL);\r
+ tv.setError("A valid authorization could not be obtained");\r
+\r
+ }\r
+ \r
+ } else if (operation instanceof ExistenceCheckOperation) {\r
+ \r
+ try {\r
+ dismissDialog(DIALOG_LOGIN_PROGRESS);\r
+ } catch (IllegalArgumentException e) {\r
+ // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
+ }\r
+ \r
+ if (result.isSuccess()) {\r
+ TextView tv = (TextView) findViewById(R.id.oAuth_URL);\r
+ Log.d(TAG, "Checked access - time to save the account");\r
+ \r
+ Uri uri = Uri.parse(mBaseUrl);\r
+ String username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong(); \r
+ String accountName = username + "@" + uri.getHost();\r
+ if (uri.getPort() >= 0) {\r
+ accountName += ":" + uri.getPort();\r
+ }\r
+ // TODO - check that accountName does not exist\r
+ Account account = new Account(accountName, AccountAuthenticator.ACCOUNT_TYPE);\r
+ AccountManager accManager = AccountManager.get(this);\r
+ accManager.addAccountExplicitly(account, "", null); // with our implementation, the password is never input in the app\r
+\r
+ // Add this account as default in the preferences, if there is none\r
+ Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);\r
+ if (defaultAccount == null) {\r
+ SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();\r
+ editor.putString("select_oc_account", accountName);\r
+ editor.commit();\r
+ }\r
+\r
+ /// account data to save by the AccountManager\r
+ final Intent intent = new Intent();\r
+ intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountAuthenticator.ACCOUNT_TYPE);\r
+ intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);\r
+ intent.putExtra(AccountManager.KEY_USERDATA, username);\r
+\r
+ accManager.setAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, ((ExistenceCheckOperation) operation).getAccessToken());\r
+ \r
+ accManager.setUserData(account, AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable.getDiscoveredVersion().toString());\r
+ accManager.setUserData(account, AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);\r
+ accManager.setUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE");\r
+\r
+ setAccountAuthenticatorResult(intent.getExtras());\r
+ setResult(RESULT_OK, intent);\r
+ \r
+ /// enforce the first account synchronization\r
+ Bundle bundle = new Bundle();\r
+ bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
+ ContentResolver.requestSync(account, "org.owncloud", bundle);\r
+\r
+ finish();\r
+ \r
+ } else { \r
+ TextView tv = (TextView) findViewById(R.id.oAuth_URL);\r
+ tv.setError(result.getLogMessage());\r
+ Log.d(TAG, "Access failed: " + result.getLogMessage());\r
+ }\r
}\r
}\r
\r
public void onFailedSavingCertificate() {\r
showDialog(DIALOG_CERT_NOT_SAVED);\r
}\r
- \r
+\r
}\r
import com.owncloud.android.files.services.FileObserverService;\r
import com.owncloud.android.files.services.FileUploader;\r
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
-import com.owncloud.android.network.OwnCloudClientUtils;\r
+import com.owncloud.android.operations.CreateFolderOperation;\r
import com.owncloud.android.operations.OnRemoteOperationListener;\r
import com.owncloud.android.operations.RemoteOperation;\r
import com.owncloud.android.operations.RemoteOperationResult;\r
import com.owncloud.android.ui.fragment.OCFileListFragment;\r
\r
import com.owncloud.android.R;\r
-import eu.alefzero.webdav.WebdavClient;\r
\r
/**\r
* Displays, what files the user has available in his ownCloud.\r
private OCFileListFragment mFileList;\r
\r
private boolean mDualPane;\r
+ private Handler mHandler;\r
\r
private static final int DIALOG_SETUP_ACCOUNT = 0;\r
private static final int DIALOG_CREATE_DIR = 1;\r
public void onCreate(Bundle savedInstanceState) {\r
Log.d(getClass().toString(), "onCreate() start");\r
super.onCreate(savedInstanceState);\r
+ \r
+ mHandler = new Handler();\r
\r
/// Load of parameters from received intent\r
mCurrentDir = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); // no check necessary, mCurrenDir == null if the parameter is not in the intent\r
\r
// Create directory\r
path += directoryName + OCFile.PATH_SEPARATOR;\r
- Thread thread = new Thread(new DirectoryCreator(path, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), new Handler()));\r
- thread.start();\r
+ RemoteOperation operation = new CreateFolderOperation(path, mCurrentDir.getFileId(), mStorageManager);\r
+ operation.execute( AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), \r
+ FileDisplayActivity.this, \r
+ FileDisplayActivity.this, \r
+ mHandler,\r
+ FileDisplayActivity.this);\r
\r
dialog.dismiss();\r
\r
return !mDirectories.isEmpty();\r
}\r
\r
- private class DirectoryCreator implements Runnable {\r
- private String mTargetPath;\r
- private Account mAccount;\r
- private Handler mHandler; \r
- \r
- public DirectoryCreator(String targetPath, Account account, Handler handler) {\r
- mTargetPath = targetPath;\r
- mAccount = account;\r
- mHandler = handler;\r
- }\r
- \r
- @Override\r
- public void run() {\r
- WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());\r
- boolean created = wdc.createDirectory(mTargetPath);\r
- if (created) {\r
- mHandler.post(new Runnable() {\r
- @Override\r
- public void run() { \r
- dismissDialog(DIALOG_SHORT_WAIT);\r
- \r
- // Save new directory in local database\r
- OCFile newDir = new OCFile(mTargetPath);\r
- newDir.setMimetype("DIR");\r
- newDir.setParentId(mCurrentDir.getFileId());\r
- mStorageManager.saveFile(newDir);\r
- \r
- // Display the new folder right away\r
- mFileList.listDirectory();\r
- }\r
- });\r
- \r
- } else {\r
- mHandler.post(new Runnable() {\r
- @Override\r
- public void run() {\r
- dismissDialog(DIALOG_SHORT_WAIT);\r
- try {\r
- Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); \r
- msg.show();\r
- \r
- } catch (NotFoundException e) {\r
- Log.e(TAG, "Error while trying to show fail message " , e);\r
- }\r
- }\r
- });\r
- }\r
- }\r
- \r
- }\r
\r
// Custom array adapter to override text colors\r
private class CustomArrayAdapter<T> extends ArrayAdapter<T> {\r
\r
} else if (operation instanceof SynchronizeFileOperation) {\r
onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);\r
+ \r
+ } else if (operation instanceof CreateFolderOperation) {\r
+ onCreateFolderOperationFinish((CreateFolderOperation)operation, result);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Updates the view associated to the activity after the finish of an operation trying create a new folder\r
+ * \r
+ * @param operation Creation operation performed.\r
+ * @param result Result of the creation.\r
+ */\r
+ private void onCreateFolderOperationFinish(CreateFolderOperation operation, RemoteOperationResult result) {\r
+ if (result.isSuccess()) {\r
+ dismissDialog(DIALOG_SHORT_WAIT);\r
+ mFileList.listDirectory();\r
+ \r
+ } else {\r
+ dismissDialog(DIALOG_SHORT_WAIT);\r
+ try {\r
+ Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); \r
+ msg.show();\r
+ \r
+ } catch (NotFoundException e) {\r
+ Log.e(TAG, "Error while trying to show fail message " , e);\r
+ }\r
}\r
}\r
\r
package com.owncloud.android.ui.fragment;\r
\r
import java.io.File;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
\r
-import org.apache.commons.httpclient.methods.GetMethod;\r
-import org.apache.commons.httpclient.methods.PostMethod;\r
-import org.apache.commons.httpclient.methods.StringRequestEntity;\r
-import org.apache.commons.httpclient.params.HttpConnectionManagerParams;\r
-import org.apache.http.HttpStatus;\r
-import org.apache.http.NameValuePair;\r
-import org.apache.http.client.utils.URLEncodedUtils;\r
-import org.apache.http.message.BasicNameValuePair;\r
-import org.apache.http.protocol.HTTP;\r
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;\r
-import org.json.JSONObject;\r
+import org.apache.commons.httpclient.Credentials;\r
\r
import android.accounts.Account;\r
import android.accounts.AccountManager;\r
import android.widget.Toast;\r
\r
import com.actionbarsherlock.app.SherlockFragment;\r
-import com.owncloud.android.AccountUtils;\r
import com.owncloud.android.DisplayUtils;\r
import com.owncloud.android.authenticator.AccountAuthenticator;\r
import com.owncloud.android.datamodel.FileDataStorageManager;\r
import com.owncloud.android.files.services.FileUploader;\r
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
-import com.owncloud.android.network.OwnCloudClientUtils;\r
+import com.owncloud.android.network.BearerCredentials;\r
import com.owncloud.android.operations.OnRemoteOperationListener;\r
import com.owncloud.android.operations.RemoteOperation;\r
import com.owncloud.android.operations.RemoteOperationResult;\r
import com.owncloud.android.ui.activity.TransferServiceGetter;\r
import com.owncloud.android.ui.dialog.EditNameDialog;\r
import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;\r
-import com.owncloud.android.utils.OwnCloudVersion;\r
\r
import com.owncloud.android.R;\r
-import eu.alefzero.webdav.WebdavClient;\r
import eu.alefzero.webdav.WebdavUtils;\r
\r
/**\r
\r
} else {\r
mLastRemoteOperation = new SynchronizeFileOperation(mFile, null, mStorageManager, mAccount, true, false, getActivity());\r
- WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());\r
- mLastRemoteOperation.execute(wc, this, mHandler);\r
+ mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());\r
\r
// update ui \r
boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
mLastRemoteOperation = new RemoveFileOperation( mFile, \r
true, \r
mStorageManager);\r
- WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());\r
- mLastRemoteOperation.execute(wc, this, mHandler);\r
- \r
+ mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());\r
boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
}\r
if (mFile.getRemotePath().equals(uploadRemotePath) ||\r
renamedInUpload) {\r
if (uploadWasFine) {\r
- mFile = mStorageManager.getFileByPath(uploadRemotePath);\r
+ mFile = mStorageManager.getFileByPath(uploadRemotePath);\r
}\r
if (renamedInUpload) {\r
String newName = (new File(uploadRemotePath)).getName();\r
\r
\r
// this is a temporary class for sharing purposes, it need to be replaced in transfer service\r
+ /*\r
@SuppressWarnings("unused")\r
private class ShareRunnable implements Runnable {\r
private String mPath;\r
}\r
}\r
}\r
+ */\r
\r
public void onDismiss(EditNameDialog dialog) {\r
if (dialog.getResult()) {\r
mAccount, \r
newFilename, \r
new FileDataStorageManager(mAccount, getActivity().getContentResolver()));\r
- WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());\r
- mLastRemoteOperation.execute(wc, this, mHandler);\r
+ mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());\r
boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
}\r
*/\r
@Override\r
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
- if (operation.equals(mLastRemoteOperation)) {\r
+ if (!result.isSuccess() && result.getCode() == ResultCode.UNAUTHORIZED) {\r
+ AccountManager am = AccountManager.get(getSherlockActivity());\r
+ //am.invalidateAuthToken(AccountAuthenticator.ACCOUNT_TYPE, OwnCloudClientUtils.getAuthorizationTokenType(operation.getClient().getCredentials()));\r
+ Credentials cred = operation.getClient().getCredentials();\r
+ if (cred instanceof BearerCredentials) {\r
+ am.invalidateAuthToken(AccountAuthenticator.ACCOUNT_TYPE, ((BearerCredentials)cred).getAccessToken());\r
+ } else {\r
+ am.clearPassword(mAccount);\r
+ }\r
+ operation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity()); // need a new client instance, so avoid retry()\r
+ // TODO si el usuario no se autoriza de nuevo, esto genera un bucle infinito; o un error en la creación del objecto cliente\r
+ \r
+ } else {\r
if (operation instanceof RemoveFileOperation) {\r
onRemoveFileOperationFinish((RemoveFileOperation)operation, result);\r
\r
case R.id.download_file_item: {
Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity());
RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, false, getSherlockActivity());
- WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(account, getSherlockActivity().getApplicationContext());
- operation.execute(wc, mContainerActivity, mHandler);
+ operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
getSherlockActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
return true;
}
AccountUtils.getCurrentOwnCloudAccount(getActivity()),
newFilename,
mContainerActivity.getStorageManager());
- WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity().getApplicationContext());
- operation.execute(wc, mContainerActivity, mHandler);
+ operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
}
}
RemoteOperation operation = new RemoveFileOperation( mTargetFile,
true,
mContainerActivity.getStorageManager());
- WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity().getApplicationContext());
- operation.execute(wc, mContainerActivity, mHandler);
+ operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
}
import java.io.FileOutputStream;\r
import java.io.IOException;\r
import java.io.InputStream;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
\r
import org.apache.commons.httpclient.Credentials;\r
+import org.apache.commons.httpclient.HostConfiguration;\r
import org.apache.commons.httpclient.HttpClient;\r
import org.apache.commons.httpclient.HttpConnectionManager;\r
import org.apache.commons.httpclient.HttpException;\r
+import org.apache.commons.httpclient.HttpMethod;\r
import org.apache.commons.httpclient.HttpMethodBase;\r
+import org.apache.commons.httpclient.HttpState;\r
import org.apache.commons.httpclient.HttpVersion;\r
import org.apache.commons.httpclient.UsernamePasswordCredentials;\r
+import org.apache.commons.httpclient.auth.AuthPolicy;\r
import org.apache.commons.httpclient.auth.AuthScope;\r
import org.apache.commons.httpclient.methods.GetMethod;\r
import org.apache.commons.httpclient.methods.HeadMethod;\r
import org.apache.http.params.CoreProtocolPNames;\r
import org.apache.jackrabbit.webdav.client.methods.DavMethod;\r
import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;\r
-import org.apache.jackrabbit.webdav.client.methods.MkColMethod;\r
+\r
+import com.owncloud.android.network.BearerAuthScheme;\r
+import com.owncloud.android.network.BearerCredentials;\r
\r
import android.net.Uri;\r
import android.util.Log;\r
getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);\r
}\r
\r
- public void setCredentials(String username, String password) {\r
- getParams().setAuthenticationPreemptive(true);\r
- getState().setCredentials(AuthScope.ANY,\r
- getCredentials(username, password));\r
+ public void setBearerCredentials(String accessToken) {\r
+ AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class);\r
+ \r
+ List<String> authPrefs = new ArrayList<String>(1);\r
+ authPrefs.add(BearerAuthScheme.AUTH_POLICY);\r
+ getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); \r
+ \r
+ mCredentials = new BearerCredentials(accessToken);\r
+ getState().setCredentials(AuthScope.ANY, mCredentials);\r
}\r
\r
- private Credentials getCredentials(String username, String password) {\r
- if (mCredentials == null)\r
- mCredentials = new UsernamePasswordCredentials(username, password);\r
- return mCredentials;\r
+ public void setBasicCredentials(String username, String password) {\r
+ List<String> authPrefs = new ArrayList<String>(1);\r
+ authPrefs.add(AuthPolicy.BASIC);\r
+ getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); \r
+ \r
+ getParams().setAuthenticationPreemptive(true);\r
+ mCredentials = new UsernamePasswordCredentials(username, password);\r
+ getState().setCredentials(AuthScope.ANY, mCredentials);\r
}\r
\r
+ \r
/**\r
* Downloads a file in remoteFilepath to the local targetPath.\r
* \r
return status;\r
}\r
\r
- /**\r
- * Creates a remote directory with the received path.\r
- * \r
- * @param path Path of the directory to create, URL DECODED\r
- * @return 'True' when the directory is successfully created\r
- */\r
- public boolean createDirectory(String path) {\r
- boolean result = false;\r
- int status = -1;\r
- MkColMethod mkcol = new MkColMethod(mUri.toString() + WebdavUtils.encodePath(path));\r
- try {\r
- Log.d(TAG, "Creating directory " + path);\r
- status = executeMethod(mkcol);\r
- Log.d(TAG, "Status returned: " + status);\r
- result = mkcol.succeeded();\r
- \r
- Log.d(TAG, "MKCOL to " + path + " finished with HTTP status " + status + (!result?"(FAIL)":""));\r
- exhaustResponse(mkcol.getResponseBodyAsStream());\r
- \r
- } catch (Exception e) {\r
- logException(e, "creating directory " + path);\r
- \r
- } finally {\r
- mkcol.releaseConnection(); // let the connection available for other methods\r
- }\r
- return result;\r
- }\r
- \r
\r
/**\r
* Check if a file exists in the OC server\r
public Uri getBaseUri() {\r
return mUri;\r
}\r
+ \r
+\r
+ @Override\r
+ public int executeMethod(HostConfiguration hostconfig, final HttpMethod method, final HttpState state) throws IOException, HttpException {\r
+ if (mCredentials instanceof BearerAuthScheme) {\r
+ method.getHostAuthState().setAuthScheme(AuthPolicy.getAuthScheme(BearerAuthScheme.AUTH_POLICY));\r
+ }\r
+ return super.executeMethod(hostconfig, method, state);\r
+ }\r
+\r
+ \r
+ public final Credentials getCredentials() {\r
+ return mCredentials;\r
+ }\r
\r
}\r