Merge branch 'develop' into operations_service
[pub/Android/ownCloud.git] / src / com / owncloud / android / authentication / AuthenticatorActivity.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 * Copyright (C) 2012-2013 ownCloud Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19 package com.owncloud.android.authentication;
20
21 import java.security.cert.X509Certificate;
22 import java.util.Map;
23
24 import android.accounts.Account;
25 import android.accounts.AccountManager;
26 import android.app.Dialog;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.ServiceConnection;
31 import android.content.SharedPreferences;
32 import android.graphics.Rect;
33 import android.graphics.drawable.Drawable;
34 import android.net.Uri;
35 import android.net.http.SslError;
36 import android.os.Bundle;
37 import android.os.Handler;
38 import android.os.IBinder;
39 import android.preference.PreferenceManager;
40 import android.support.v4.app.Fragment;
41 import android.support.v4.app.FragmentManager;
42 import android.support.v4.app.FragmentTransaction;
43 import android.text.Editable;
44 import android.text.InputType;
45 import android.text.TextWatcher;
46 import android.view.KeyEvent;
47 import android.view.MotionEvent;
48 import android.view.View;
49 import android.view.View.OnFocusChangeListener;
50 import android.view.View.OnTouchListener;
51 import android.view.Window;
52 import android.view.inputmethod.EditorInfo;
53 import android.webkit.SslErrorHandler;
54 import android.widget.Button;
55 import android.widget.CheckBox;
56 import android.widget.EditText;
57 import android.widget.TextView;
58 import android.widget.TextView.OnEditorActionListener;
59 import android.widget.Toast;
60
61 import com.actionbarsherlock.app.SherlockDialogFragment;
62 import com.owncloud.android.MainApp;
63 import com.owncloud.android.R;
64 import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;
65 import com.owncloud.android.lib.common.accounts.AccountTypeUtils;
66 import com.owncloud.android.lib.common.accounts.AccountUtils.Constants;
67 import com.owncloud.android.operations.DetectAuthenticationMethodOperation.AuthenticationMethod;
68 import com.owncloud.android.operations.GetServerInfoOperation;
69 import com.owncloud.android.operations.OAuth2GetAccessToken;
70
71 import com.owncloud.android.lib.common.network.CertificateCombinedException;
72 import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
73 import com.owncloud.android.lib.common.operations.RemoteOperation;
74 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
75 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
76 import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
77 import com.owncloud.android.lib.resources.users.GetRemoteUserNameOperation;
78
79 import com.owncloud.android.services.OperationsService;
80 import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
81 import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
82 import com.owncloud.android.ui.dialog.SamlWebViewDialog;
83 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
84 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
85 import com.owncloud.android.utils.Log_OC;
86 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
87
88 /**
89 * This Activity is used to add an ownCloud account to the App
90 *
91 * @author Bartek Przybylski
92 * @author David A. Velasco
93 * @author masensio
94 */
95 public class AuthenticatorActivity extends AccountAuthenticatorActivity
96 implements OnRemoteOperationListener, OnFocusChangeListener, OnEditorActionListener,
97 SsoWebViewClientListener, OnSslUntrustedCertListener {
98
99 private static final String TAG = AuthenticatorActivity.class.getSimpleName();
100
101 public static final String EXTRA_ACCOUNT = "ACCOUNT";
102 public static final String EXTRA_USER_NAME = "USER_NAME";
103 public static final String EXTRA_HOST_NAME = "HOST_NAME";
104 public static final String EXTRA_ACTION = "ACTION";
105 public static final String EXTRA_ENFORCED_UPDATE = "ENFORCE_UPDATE";
106
107 private static final String KEY_AUTH_MESSAGE_VISIBILITY = "AUTH_MESSAGE_VISIBILITY";
108 private static final String KEY_AUTH_MESSAGE_TEXT = "AUTH_MESSAGE_TEXT";
109 private static final String KEY_HOST_URL_TEXT = "HOST_URL_TEXT";
110 private static final String KEY_OC_VERSION = "OC_VERSION";
111 private static final String KEY_ACCOUNT = "ACCOUNT";
112 private static final String KEY_SERVER_VALID = "SERVER_VALID";
113 private static final String KEY_SERVER_CHECKED = "SERVER_CHECKED";
114 private static final String KEY_SERVER_CHECK_IN_PROGRESS = "SERVER_CHECK_IN_PROGRESS";
115 private static final String KEY_SERVER_STATUS_TEXT = "SERVER_STATUS_TEXT";
116 private static final String KEY_SERVER_STATUS_ICON = "SERVER_STATUS_ICON";
117 private static final String KEY_IS_SSL_CONN = "IS_SSL_CONN";
118 private static final String KEY_PASSWORD_VISIBLE = "PASSWORD_VISIBLE";
119 private static final String KEY_AUTH_STATUS_TEXT = "AUTH_STATUS_TEXT";
120 private static final String KEY_AUTH_STATUS_ICON = "AUTH_STATUS_ICON";
121 private static final String KEY_REFRESH_BUTTON_ENABLED = "KEY_REFRESH_BUTTON_ENABLED";
122 private static final String KEY_SERVER_AUTH_METHOD = "KEY_SERVER_AUTH_METHOD";
123 private static final String KEY_DETECT_AUTH_OP_ID = "KEY_DETECT_AUTH_OP_ID";
124
125 private static final String AUTH_ON = "on";
126 //private static final String AUTH_OFF = "off";
127 private static final String AUTH_OPTIONAL = "optional";
128
129 public static final byte ACTION_CREATE = 0;
130 public static final byte ACTION_UPDATE_TOKEN = 1;
131
132 private static final String SAML_DIALOG_TAG = "SAML_DIALOG";
133 private static final String WAIT_DIALOG_TAG = "WAIT_DIALOG";
134
135 private String mHostBaseUrl; // TODO remove
136 private OwnCloudVersion mDiscoveredVersion; // TODO remove
137
138 private String mAuthMessageText;
139 private int mAuthMessageVisibility, mServerStatusText, mServerStatusIcon;
140 private boolean mServerIsChecked, mServerIsValid, mIsSslConn;
141 private AuthenticationMethod mServerAuthMethod = AuthenticationMethod.UNKNOWN;
142
143 private int mGetServerInfoOpId = -1;
144 private int mOauth2GetAccessTokenOpId = -1;
145
146 private int mAuthStatusText, mAuthStatusIcon;
147 private TextView mAuthStatusLayout;
148
149 private final Handler mHandler = new Handler();
150 private GetServerInfoOperation mServerInfoOperation;
151
152 private int mExistenceCheckOpId = -1;
153 private int mGetUserNameOpId = -1;
154
155 private Uri mNewCapturedUriFromOAuth2Redirection;
156
157 private AccountManager mAccountMgr;
158 private boolean mJustCreated;
159 private byte mAction;
160 private Account mAccount;
161
162 private TextView mAuthMessage;
163
164 private EditText mHostUrlInput;
165 private boolean mHostUrlInputEnabled;
166 private View mRefreshButton;
167
168 private String mAuthTokenType;
169
170 private EditText mUsernameInput;
171 private EditText mPasswordInput;
172
173 private CheckBox mOAuth2Check;
174
175 private TextView mOAuthAuthEndpointText;
176 private TextView mOAuthTokenEndpointText;
177
178 private SamlWebViewDialog mSamlDialog;
179
180 private View mOkButton;
181
182 private String mAuthToken;
183
184 private boolean mResumed; // Control if activity is resumed
185
186 public static String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT";
187
188 private ServiceConnection mOperationsServiceConnection = null;
189
190 private OperationsServiceBinder mOperationsServiceBinder = null;
191
192 private GetServerInfoOperation.ServerInfo mServerInfo;
193
194 /**
195 * {@inheritDoc}
196 *
197 * IMPORTANT ENTRY POINT 1: activity is shown to the user
198 */
199 @Override
200 protected void onCreate(Bundle savedInstanceState) {
201 super.onCreate(savedInstanceState);
202 getWindow().requestFeature(Window.FEATURE_NO_TITLE);
203
204 // bind to Operations Service
205 mOperationsServiceConnection = new OperationsServiceConnection();
206 if (!bindService(new Intent(this, OperationsService.class),
207 mOperationsServiceConnection,
208 Context.BIND_AUTO_CREATE)) {
209 Toast.makeText(this,
210 R.string.error_cant_bind_to_operations_service,
211 Toast.LENGTH_LONG)
212 .show();
213 finish();
214 }
215
216 /// set view and get references to view elements
217 setContentView(R.layout.account_setup);
218 mAuthMessage = (TextView) findViewById(R.id.auth_message);
219 mHostUrlInput = (EditText) findViewById(R.id.hostUrlInput);
220 mHostUrlInput.setText(getString(R.string.server_url)); // valid although R.string.server_url is an empty string
221 mUsernameInput = (EditText) findViewById(R.id.account_username);
222 mPasswordInput = (EditText) findViewById(R.id.account_password);
223 mOAuthAuthEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_1);
224 mOAuthTokenEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_2);
225 mOAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);
226 mOkButton = findViewById(R.id.buttonOK);
227 mAuthStatusLayout = (TextView) findViewById(R.id.auth_status_text);
228
229 /// set Host Url Input Enabled
230 mHostUrlInputEnabled = getResources().getBoolean(R.bool.show_server_url_input);
231
232 /// set visibility of link for new users
233 boolean accountRegisterVisibility = getResources().getBoolean(R.bool.show_welcome_link);
234 Button welcomeLink = (Button) findViewById(R.id.welcome_link);
235 if (welcomeLink != null) {
236 if (accountRegisterVisibility) {
237 welcomeLink.setVisibility(View.VISIBLE);
238 welcomeLink.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name)));
239 } else {
240 findViewById(R.id.welcome_link).setVisibility(View.GONE);
241 }
242 }
243
244 /// initialization
245 mAccountMgr = AccountManager.get(this);
246 mNewCapturedUriFromOAuth2Redirection = null;
247 mAction = getIntent().getByteExtra(EXTRA_ACTION, ACTION_CREATE);
248 mAccount = null;
249 mHostBaseUrl = "";
250 boolean refreshButtonEnabled = false;
251
252 // URL input configuration applied
253 if (!mHostUrlInputEnabled)
254 {
255 findViewById(R.id.hostUrlFrame).setVisibility(View.GONE);
256 mRefreshButton = findViewById(R.id.centeredRefreshButton);
257
258 } else {
259 mRefreshButton = findViewById(R.id.embeddedRefreshButton);
260 }
261
262 if (savedInstanceState == null) {
263 mResumed = false;
264 /// connection state and info
265 mAuthMessageVisibility = View.GONE;
266 mServerStatusText = mServerStatusIcon = 0;
267 mServerIsValid = false;
268 mServerIsChecked = false;
269 mIsSslConn = false;
270 mAuthStatusText = mAuthStatusIcon = 0;
271
272 /// retrieve extras from intent
273 mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);
274 if (mAccount != null) {
275 String ocVersion = mAccountMgr.getUserData(mAccount, Constants.KEY_OC_VERSION);
276 if (ocVersion != null) {
277 mDiscoveredVersion = new OwnCloudVersion(ocVersion);
278 }
279 mHostBaseUrl = normalizeUrl(mAccountMgr.getUserData(mAccount, Constants.KEY_OC_BASE_URL));
280 mHostUrlInput.setText(mHostBaseUrl);
281 String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));
282 mUsernameInput.setText(userName);
283
284 }
285 initAuthorizationMethod(); // checks intent and setup.xml to determine mCurrentAuthorizationMethod
286 mJustCreated = true;
287
288 if (mAction == ACTION_UPDATE_TOKEN || !mHostUrlInputEnabled) {
289 checkOcServer();
290 }
291
292 } else {
293 mResumed = true;
294 /// connection state and info
295 mAuthMessageVisibility = savedInstanceState.getInt(KEY_AUTH_MESSAGE_VISIBILITY);
296 mAuthMessageText = savedInstanceState.getString(KEY_AUTH_MESSAGE_TEXT);
297 mServerIsValid = savedInstanceState.getBoolean(KEY_SERVER_VALID);
298 mServerIsChecked = savedInstanceState.getBoolean(KEY_SERVER_CHECKED);
299 mServerStatusText = savedInstanceState.getInt(KEY_SERVER_STATUS_TEXT);
300 mServerStatusIcon = savedInstanceState.getInt(KEY_SERVER_STATUS_ICON);
301 mIsSslConn = savedInstanceState.getBoolean(KEY_IS_SSL_CONN);
302 mAuthStatusText = savedInstanceState.getInt(KEY_AUTH_STATUS_TEXT);
303 mAuthStatusIcon = savedInstanceState.getInt(KEY_AUTH_STATUS_ICON);
304 if (savedInstanceState.getBoolean(KEY_PASSWORD_VISIBLE, false)) {
305 showPassword();
306 }
307
308 /// server data
309 String ocVersion = savedInstanceState.getString(KEY_OC_VERSION);
310 if (ocVersion != null) {
311 mDiscoveredVersion = new OwnCloudVersion(ocVersion);
312 }
313 mHostBaseUrl = savedInstanceState.getString(KEY_HOST_URL_TEXT);
314
315 // account data, if updating
316 mAccount = savedInstanceState.getParcelable(KEY_ACCOUNT);
317 mAuthTokenType = savedInstanceState.getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);
318 if (mAuthTokenType == null) {
319 mAuthTokenType = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType());
320
321 }
322
323 // check if server check was interrupted by a configuration change
324 if (savedInstanceState.getBoolean(KEY_SERVER_CHECK_IN_PROGRESS, false)) {
325 checkOcServer();
326 }
327
328 // refresh button enabled
329 refreshButtonEnabled = savedInstanceState.getBoolean(KEY_REFRESH_BUTTON_ENABLED);
330
331
332 mServerAuthMethod = AuthenticationMethod.valueOf(
333 savedInstanceState.getString(KEY_SERVER_AUTH_METHOD));
334 mGetServerInfoOpId = savedInstanceState.getInt(KEY_DETECT_AUTH_OP_ID);
335
336 }
337
338 if (mAuthMessageVisibility== View.VISIBLE) {
339 showAuthMessage(mAuthMessageText);
340 }
341 else {
342 hideAuthMessage();
343 }
344 adaptViewAccordingToAuthenticationMethod();
345 showServerStatus();
346 showAuthStatus();
347
348 if (mAction == ACTION_UPDATE_TOKEN) {
349 /// lock things that should not change
350 mHostUrlInput.setEnabled(false);
351 mHostUrlInput.setFocusable(false);
352 mUsernameInput.setEnabled(false);
353 mUsernameInput.setFocusable(false);
354 mOAuth2Check.setVisibility(View.GONE);
355 }
356
357 showRefreshButton(mServerIsChecked && !mServerIsValid && refreshButtonEnabled);
358 mOkButton.setEnabled(mServerIsValid); // state not automatically recovered in configuration changes
359
360 if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType) ||
361 !AUTH_OPTIONAL.equals(getString(R.string.auth_method_oauth2))) {
362 mOAuth2Check.setVisibility(View.GONE);
363 }
364
365 mPasswordInput.setText(""); // clean password to avoid social hacking (disadvantage: password in removed if the device is turned aside)
366
367 /// bind view elements to listeners and other friends
368 mHostUrlInput.setOnFocusChangeListener(this);
369 mHostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
370 mHostUrlInput.setOnEditorActionListener(this);
371 mHostUrlInput.addTextChangedListener(new TextWatcher() {
372
373 @Override
374 public void afterTextChanged(Editable s) {
375 if (!mHostBaseUrl.equals(normalizeUrl(mHostUrlInput.getText().toString()))) {
376 mOkButton.setEnabled(false);
377 }
378 }
379
380 @Override
381 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
382 }
383
384 @Override
385 public void onTextChanged(CharSequence s, int start, int before, int count) {
386 if (!mResumed) {
387 mAuthStatusIcon = 0;
388 mAuthStatusText = 0;
389 showAuthStatus();
390 }
391 mResumed = false;
392 }
393 });
394
395 mPasswordInput.setOnFocusChangeListener(this);
396 mPasswordInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
397 mPasswordInput.setOnEditorActionListener(this);
398 mPasswordInput.setOnTouchListener(new RightDrawableOnTouchListener() {
399 @Override
400 public boolean onDrawableTouch(final MotionEvent event) {
401 if (event.getAction() == MotionEvent.ACTION_UP) {
402 AuthenticatorActivity.this.onViewPasswordClick();
403 }
404 return true;
405 }
406 });
407
408 findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() {
409 @Override
410 public boolean onTouch(View view, MotionEvent event) {
411 if (event.getAction() == MotionEvent.ACTION_DOWN) {
412 if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType) &&
413 mHostUrlInput.hasFocus()) {
414 checkOcServer();
415 }
416 }
417 return false;
418 }
419 });
420
421 }
422
423
424
425 private void initAuthorizationMethod() {
426 boolean oAuthRequired = false;
427 boolean samlWebSsoRequired = false;
428
429 mAuthTokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);
430 mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);
431
432 // TODO could be a good moment to validate the received token type, if not null
433
434 if (mAuthTokenType == null) {
435 if (mAccount != null) {
436 /// same authentication method than the one used to create the account to update
437 oAuthRequired = (mAccountMgr.getUserData(mAccount, Constants.KEY_SUPPORTS_OAUTH2) != null);
438 samlWebSsoRequired = (mAccountMgr.getUserData(mAccount, Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null);
439
440 } else {
441 /// use the one set in setup.xml
442 oAuthRequired = AUTH_ON.equals(getString(R.string.auth_method_oauth2));
443 samlWebSsoRequired = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso));
444 }
445 if (oAuthRequired) {
446 mAuthTokenType = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType());
447 } else if (samlWebSsoRequired) {
448 mAuthTokenType = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType());
449 } else {
450 mAuthTokenType = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType());
451 }
452 }
453
454 if (mAccount != null) {
455 String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));
456 mUsernameInput.setText(userName);
457 }
458
459 mOAuth2Check.setChecked(AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType));
460
461 }
462
463 /**
464 * Saves relevant state before {@link #onPause()}
465 *
466 * Do NOT save {@link #mNewCapturedUriFromOAuth2Redirection}; it keeps a temporal flag, intended to defer the
467 * processing of the redirection caught in {@link #onNewIntent(Intent)} until {@link #onResume()}
468 *
469 * See {@link #loadSavedInstanceState(Bundle)}
470 */
471 @Override
472 protected void onSaveInstanceState(Bundle outState) {
473 //Log.wtf(TAG, "onSaveInstanceState init" );
474 super.onSaveInstanceState(outState);
475
476 /// connection state and info
477 outState.putInt(KEY_AUTH_MESSAGE_VISIBILITY, mAuthMessage.getVisibility());
478 outState.putString(KEY_AUTH_MESSAGE_TEXT, mAuthMessage.getText().toString());
479 outState.putInt(KEY_SERVER_STATUS_TEXT, mServerStatusText);
480 outState.putInt(KEY_SERVER_STATUS_ICON, mServerStatusIcon);
481 outState.putBoolean(KEY_SERVER_VALID, mServerIsValid);
482 outState.putBoolean(KEY_SERVER_CHECKED, mServerIsChecked);
483 outState.putBoolean(KEY_SERVER_CHECK_IN_PROGRESS, (!mServerIsValid && mServerInfoOperation != null));
484 outState.putBoolean(KEY_IS_SSL_CONN, mIsSslConn);
485 outState.putBoolean(KEY_PASSWORD_VISIBLE, isPasswordVisible());
486 outState.putInt(KEY_AUTH_STATUS_ICON, mAuthStatusIcon);
487 outState.putInt(KEY_AUTH_STATUS_TEXT, mAuthStatusText);
488
489 /// server data
490 if (mDiscoveredVersion != null) {
491 outState.putString(KEY_OC_VERSION, mDiscoveredVersion.getVersion());
492 }
493 outState.putString(KEY_HOST_URL_TEXT, mHostBaseUrl);
494
495 /// account data, if updating
496 if (mAccount != null) {
497 outState.putParcelable(KEY_ACCOUNT, mAccount);
498 }
499 outState.putString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE, mAuthTokenType);
500
501 // refresh button enabled
502 outState.putBoolean(KEY_REFRESH_BUTTON_ENABLED, (mRefreshButton.getVisibility() == View.VISIBLE));
503
504 outState.putString(KEY_SERVER_AUTH_METHOD, mServerAuthMethod.name());
505 outState.putInt(KEY_DETECT_AUTH_OP_ID, mGetServerInfoOpId);
506 //Log.wtf(TAG, "onSaveInstanceState end" );
507 }
508
509
510 /**
511 * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION request
512 * is caught here.
513 *
514 * To make this possible, this activity needs to be qualified with android:launchMode = "singleTask" in the
515 * AndroidManifest.xml file.
516 */
517 @Override
518 protected void onNewIntent (Intent intent) {
519 Log_OC.d(TAG, "onNewIntent()");
520 Uri data = intent.getData();
521 if (data != null && data.toString().startsWith(getString(R.string.oauth2_redirect_uri))) {
522 mNewCapturedUriFromOAuth2Redirection = data;
523 }
524 }
525
526
527 /**
528 * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION, and
529 * deferred in {@link #onNewIntent(Intent)}, is processed here.
530 */
531 @Override
532 protected void onResume() {
533 //Log.wtf(TAG, "onResume init" );
534 super.onResume();
535
536 if (mAction == ACTION_UPDATE_TOKEN && mJustCreated && getIntent().getBooleanExtra(EXTRA_ENFORCED_UPDATE, false)) {
537 if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)) {
538 //Toast.makeText(this, R.string.auth_expired_oauth_token_toast, Toast.LENGTH_LONG).show();
539 showAuthMessage(getString(R.string.auth_expired_oauth_token_toast));
540 } else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) {
541 //Toast.makeText(this, R.string.auth_expired_saml_sso_token_toast, Toast.LENGTH_LONG).show();
542 showAuthMessage(getString(R.string.auth_expired_saml_sso_token_toast));
543 } else {
544 //Toast.makeText(this, R.string.auth_expired_basic_auth_toast, Toast.LENGTH_LONG).show();
545 showAuthMessage(getString(R.string.auth_expired_basic_auth_toast));
546 }
547 }
548
549 if (mNewCapturedUriFromOAuth2Redirection != null) {
550 getOAuth2AccessTokenFromCapturedRedirection();
551 }
552
553 mJustCreated = false;
554
555 if (mOperationsServiceBinder != null) {
556 doOnResumeAndBound();
557 }
558
559 //Log.wtf(TAG, "onResume end" );
560 }
561
562
563 @Override
564 protected void onPause() {
565 //Log.wtf(TAG, "onPause init" );
566 if (mOperationsServiceBinder != null) {
567 //Log.wtf(TAG, "unregistering to listen for operation callbacks" );
568 mOperationsServiceBinder.removeOperationListener(this);
569 }
570 super.onPause();
571 //Log.wtf(TAG, "onPause end" );
572 }
573
574 @Override
575 protected void onDestroy() {
576 if (mOperationsServiceConnection != null) {
577 unbindService(mOperationsServiceConnection);
578 mOperationsServiceBinder = null;
579 }
580 super.onDestroy();
581 }
582
583
584 /**
585 * Parses the redirection with the response to the GET AUTHORIZATION request to the
586 * oAuth server and requests for the access token (GET ACCESS TOKEN)
587 */
588 private void getOAuth2AccessTokenFromCapturedRedirection() {
589 /// Parse data from OAuth redirection
590 String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();
591 mNewCapturedUriFromOAuth2Redirection = null;
592
593 /// Showing the dialog with instructions for the user.
594 IndeterminateProgressDialog dialog =
595 IndeterminateProgressDialog.newInstance(R.string.auth_getting_authorization, true);
596 dialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
597
598 /// GET ACCESS TOKEN to the oAuth server
599 Intent getServerInfoIntent = new Intent();
600 getServerInfoIntent.setAction(OperationsService.ACTION_OAUTH2_GET_ACCESS_TOKEN);
601
602 getServerInfoIntent.putExtra(
603 OperationsService.EXTRA_SERVER_URL,
604 mOAuthTokenEndpointText.getText().toString().trim());
605
606 getServerInfoIntent.putExtra(
607 OperationsService.EXTRA_OAUTH2_QUERY_PARAMETERS,
608 queryParameters);
609
610 if (mOperationsServiceBinder != null) {
611 //Log.wtf(TAG, "getting access token..." );
612 mOauth2GetAccessTokenOpId = mOperationsServiceBinder.newOperation(getServerInfoIntent);
613 }
614 }
615
616
617
618 /**
619 * Handles the change of focus on the text inputs for the server URL and the password
620 */
621 public void onFocusChange(View view, boolean hasFocus) {
622 if (view.getId() == R.id.hostUrlInput) {
623 if (!hasFocus) {
624 onUrlInputFocusLost((TextView) view);
625 }
626 else {
627 showRefreshButton(false);
628 }
629
630 } else if (view.getId() == R.id.account_password) {
631 onPasswordFocusChanged((TextView) view, hasFocus);
632 }
633 }
634
635
636 /**
637 * Handles changes in focus on the text input for the server URL.
638 *
639 * IMPORTANT ENTRY POINT 2: When (!hasFocus), user wrote the server URL and changed to
640 * other field. The operation to check the existence of the server in the entered URL is
641 * started.
642 *
643 * When hasFocus: user 'comes back' to write again the server URL.
644 *
645 * @param hostInput TextView with the URL input field receiving the change of focus.
646 */
647 private void onUrlInputFocusLost(TextView hostInput) {
648 if (!mHostBaseUrl.equals(normalizeUrl(mHostUrlInput.getText().toString()))) {
649 checkOcServer();
650 } else {
651 mOkButton.setEnabled(mServerIsValid);
652 showRefreshButton(!mServerIsValid);
653 }
654 }
655
656
657 private void checkOcServer() {
658 String uri = mHostUrlInput.getText().toString().trim();
659
660 if (!mHostUrlInputEnabled){
661 uri = getString(R.string.server_url).trim();
662 }
663
664 mServerIsValid = false;
665 mServerIsChecked = false;
666 mOkButton.setEnabled(false);
667 mDiscoveredVersion = null;
668 mServerAuthMethod = AuthenticationMethod.UNKNOWN;
669 showRefreshButton(false);
670
671 if (uri.length() != 0) {
672 mServerStatusText = R.string.auth_testing_connection;
673 mServerStatusIcon = R.drawable.progress_small;
674 showServerStatus();
675
676 Intent getServerInfoIntent = new Intent();
677 getServerInfoIntent.setAction(OperationsService.ACTION_GET_SERVER_INFO);
678 getServerInfoIntent.putExtra(OperationsService.EXTRA_SERVER_URL, uri);
679 getServerInfoIntent.putExtra(OperationsService.EXTRA_AUTH_TOKEN_TYPE, mAuthTokenType);
680 if (mOperationsServiceBinder != null) {
681 //Log.wtf(TAG, "checking server..." );
682 mGetServerInfoOpId = mOperationsServiceBinder.newOperation(getServerInfoIntent);
683 }
684
685 } else {
686 mServerStatusText = 0;
687 mServerStatusIcon = 0;
688 showServerStatus();
689 }
690 }
691
692
693 /**
694 * Handles changes in focus on the text input for the password (basic authorization).
695 *
696 * When (hasFocus), the button to toggle password visibility is shown.
697 *
698 * When (!hasFocus), the button is made invisible and the password is hidden.
699 *
700 * @param passwordInput TextView with the password input field receiving the change of focus.
701 * @param hasFocus 'True' if focus is received, 'false' if is lost
702 */
703 private void onPasswordFocusChanged(TextView passwordInput, boolean hasFocus) {
704 if (hasFocus) {
705 showViewPasswordButton();
706 } else {
707 hidePassword();
708 hidePasswordButton();
709 }
710 }
711
712
713 private void showViewPasswordButton() {
714 //int drawable = android.R.drawable.ic_menu_view;
715 int drawable = R.drawable.ic_view;
716 if (isPasswordVisible()) {
717 //drawable = android.R.drawable.ic_secure;
718 drawable = R.drawable.ic_hide;
719 }
720 mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, drawable, 0);
721 }
722
723 private boolean isPasswordVisible() {
724 return ((mPasswordInput.getInputType() & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
725 }
726
727 private void hidePasswordButton() {
728 mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
729 }
730
731 private void showPassword() {
732 mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
733 showViewPasswordButton();
734 }
735
736 private void hidePassword() {
737 mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
738 showViewPasswordButton();
739 }
740
741 /**
742 * Checks the credentials of the user in the root of the ownCloud server
743 * before creating a new local account.
744 *
745 * For basic authorization, a check of existence of the root folder is
746 * performed.
747 *
748 * For OAuth, starts the flow to get an access token; the credentials test
749 * is postponed until it is available.
750 *
751 * IMPORTANT ENTRY POINT 4
752 *
753 * @param view OK button
754 */
755 public void onOkClick(View view) {
756 // this check should be unnecessary
757 if (mDiscoveredVersion == null || !mDiscoveredVersion.isVersionValid() || mHostBaseUrl == null || mHostBaseUrl.length() == 0) {
758 mServerStatusIcon = R.drawable.common_error;
759 mServerStatusText = R.string.auth_wtf_reenter_URL;
760 showServerStatus();
761 mOkButton.setEnabled(false);
762 Log_OC.wtf(TAG, "The user was allowed to click 'connect' to an unchecked server!!");
763 return;
764 }
765
766 if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)) {
767 startOauthorization();
768 } else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) {
769 startSamlBasedFederatedSingleSignOnAuthorization();
770 } else {
771 checkBasicAuthorization();
772 }
773 }
774
775
776 /**
777 * Tests the credentials entered by the user performing a check of existence on
778 * the root folder of the ownCloud server.
779 */
780 private void checkBasicAuthorization() {
781 /// get the path to the root folder through WebDAV from the version server
782 String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);
783
784 /// get basic credentials entered by user
785 String username = mUsernameInput.getText().toString();
786 String password = mPasswordInput.getText().toString();
787
788 /// be gentle with the user
789 IndeterminateProgressDialog dialog =
790 IndeterminateProgressDialog.newInstance(R.string.auth_trying_to_login, true);
791 dialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
792
793 /// test credentials accessing the root folder
794 String remotePath ="";
795 boolean successIfAbsent = false;
796 boolean followRedirects = true;
797 startExistenceCheckRemoteOperation(remotePath, this, successIfAbsent, webdav_path, username, password, followRedirects);
798
799 }
800
801 private void startExistenceCheckRemoteOperation(String remotePath, Context context, boolean successIfAbsent, String webdav_path,
802 String username, String password, boolean followRedirects) {
803
804 Intent existenceCheckIntent = new Intent();
805 existenceCheckIntent.setAction(OperationsService.ACTION_EXISTENCE_CHECK);
806 existenceCheckIntent.putExtra(OperationsService.EXTRA_SERVER_URL, mHostBaseUrl);
807 existenceCheckIntent.putExtra(OperationsService.EXTRA_REMOTE_PATH, remotePath);
808 existenceCheckIntent.putExtra(OperationsService.EXTRA_SUCCESS_IF_ABSENT, successIfAbsent);
809 existenceCheckIntent.putExtra(OperationsService.EXTRA_WEBDAV_PATH, webdav_path);
810 existenceCheckIntent.putExtra(OperationsService.EXTRA_USERNAME, username);
811 existenceCheckIntent.putExtra(OperationsService.EXTRA_PASSWORD, password);
812 existenceCheckIntent.putExtra(OperationsService.EXTRA_AUTH_TOKEN, mAuthToken);
813 existenceCheckIntent.putExtra(OperationsService.EXTRA_FOLLOW_REDIRECTS, followRedirects);
814
815 if (mOperationsServiceBinder != null) {
816 Log_OC.wtf(TAG, "starting existenceCheckRemoteOperation..." );
817 mExistenceCheckOpId = mOperationsServiceBinder.newOperation(existenceCheckIntent);
818 }
819 }
820
821 /**
822 * Starts the OAuth 'grant type' flow to get an access token, with
823 * a GET AUTHORIZATION request to the BUILT-IN authorization server.
824 */
825 private void startOauthorization() {
826 // be gentle with the user
827 mAuthStatusIcon = R.drawable.progress_small;
828 mAuthStatusText = R.string.oauth_login_connection;
829 showAuthStatus();
830
831 // GET AUTHORIZATION request
832 //Uri uri = Uri.parse(getString(R.string.oauth2_url_endpoint_auth));
833 Uri uri = Uri.parse(mOAuthAuthEndpointText.getText().toString().trim());
834 Uri.Builder uriBuilder = uri.buildUpon();
835 uriBuilder.appendQueryParameter(OAuth2Constants.KEY_RESPONSE_TYPE, getString(R.string.oauth2_response_type));
836 uriBuilder.appendQueryParameter(OAuth2Constants.KEY_REDIRECT_URI, getString(R.string.oauth2_redirect_uri));
837 uriBuilder.appendQueryParameter(OAuth2Constants.KEY_CLIENT_ID, getString(R.string.oauth2_client_id));
838 uriBuilder.appendQueryParameter(OAuth2Constants.KEY_SCOPE, getString(R.string.oauth2_scope));
839 //uriBuilder.appendQueryParameter(OAuth2Constants.KEY_STATE, whateverwewant);
840 uri = uriBuilder.build();
841 Log_OC.d(TAG, "Starting browser to view " + uri.toString());
842 Intent i = new Intent(Intent.ACTION_VIEW, uri);
843 startActivity(i);
844 }
845
846
847 /**
848 * Starts the Web Single Sign On flow to get access to the root folder
849 * in the server.
850 */
851 private void startSamlBasedFederatedSingleSignOnAuthorization() {
852 // be gentle with the user
853 mAuthStatusIcon = R.drawable.progress_small;
854 mAuthStatusText = R.string.auth_connecting_auth_server;
855 showAuthStatus();
856 IndeterminateProgressDialog dialog =
857 IndeterminateProgressDialog.newInstance(R.string.auth_trying_to_login, true);
858 dialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
859
860 /// get the path to the root folder through WebDAV from the version server
861 String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);
862
863 /// test credentials accessing the root folder
864 String remotePath ="";
865 boolean successIfAbsent = false;
866 boolean followRedirections = false;
867 startExistenceCheckRemoteOperation(remotePath, this, successIfAbsent, webdav_path, "", "", followRedirections);
868
869 }
870
871 /**
872 * Callback method invoked when a RemoteOperation executed by this Activity finishes.
873 *
874 * Dispatches the operation flow to the right method.
875 */
876 @Override
877 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
878
879 if (operation instanceof GetServerInfoOperation) {
880 if (operation.hashCode() == mGetServerInfoOpId) {
881 onGetServerInfoFinish(result);
882 } // else nothing ; only the last check operation is considered;
883 // multiple can be started if the user amends a URL quickly
884
885 } else if (operation instanceof OAuth2GetAccessToken) {
886 onGetOAuthAccessTokenFinish(result);
887
888 } else if (operation instanceof ExistenceCheckRemoteOperation) {
889 Log_OC.wtf(TAG, "received detection response through callback" );
890 if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) {
891 onSamlBasedFederatedSingleSignOnAuthorizationStart(result);
892
893 } else {
894 onAuthorizationCheckFinish(result);
895 }
896 } else if (operation instanceof GetRemoteUserNameOperation) {
897 onGetUserNameFinish(result);
898 }
899
900 }
901
902 private void onGetUserNameFinish(RemoteOperationResult result) {
903 mGetUserNameOpId = -1;
904 if (result.isSuccess()) {
905 boolean success = false;
906 String username = (String) result.getData().get(0);
907
908 if ( mAction == ACTION_CREATE) {
909 mUsernameInput.setText(username);
910 success = createAccount();
911 } else {
912
913 if (!mUsernameInput.getText().toString().equals(username)) {
914 // fail - not a new account, but an existing one; disallow
915 result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME);
916 updateAuthStatusIconAndText(result);
917 showAuthStatus();
918 Log_OC.d(TAG, result.getLogMessage());
919 } else {
920 updateToken();
921 success = true;
922 }
923 }
924
925 if (success)
926 finish();
927 } else {
928 updateStatusIconFailUserName();
929 showAuthStatus();
930 Log_OC.e(TAG, "Access to user name failed: " + result.getLogMessage());
931 }
932
933 }
934
935 private void onSamlBasedFederatedSingleSignOnAuthorizationStart(RemoteOperationResult result) {
936 mExistenceCheckOpId = -1;
937 dismissDialog(WAIT_DIALOG_TAG);
938
939 //if (result.isTemporalRedirection() && result.isIdPRedirection()) {
940 if (result.isIdPRedirection()) {
941 String url = result.getRedirectedLocation();
942 String targetUrl = mHostBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);
943
944 // Show dialog
945 mSamlDialog = SamlWebViewDialog.newInstance(url, targetUrl);
946 mSamlDialog.show(getSupportFragmentManager(), SAML_DIALOG_TAG);
947
948 mAuthStatusIcon = 0;
949 mAuthStatusText = 0;
950
951 } else {
952 mAuthStatusIcon = R.drawable.common_error;
953 mAuthStatusText = R.string.auth_unsupported_auth_method;
954
955 }
956 showAuthStatus();
957 }
958
959
960 /**
961 * Processes the result of the server check performed when the user finishes the enter of the
962 * server URL.
963 *
964 * @param operation Server check performed.
965 * @param result Result of the check.
966 */
967 private void onGetServerInfoFinish(RemoteOperationResult result) {
968 /// update activity state
969 mServerIsChecked = true;
970 mIsSslConn = (result.getCode() == ResultCode.OK_SSL);
971 mServerInfoOperation = null;
972 mGetServerInfoOpId = -1;
973
974 // update server status, but don't show it yet
975 updateServerStatusIconAndText(result);
976
977 if (result.isSuccess()) {
978 /// SUCCESS means:
979 // 1. connection succeeded, and we know if it's SSL or not
980 // 2. server is installed
981 // 3. we got the server version
982 // 4. we got the authentication method required by the server
983 mServerInfo = (GetServerInfoOperation.ServerInfo) (result.getData().get(0));
984 mDiscoveredVersion = mServerInfo.mVersion;
985 mHostBaseUrl = mServerInfo.mBaseUrl;
986 mServerAuthMethod = mServerInfo.mAuthMethod;
987
988 if (!authSupported(mServerAuthMethod)) {
989
990 updateServerStatusIconNoRegularAuth(); // overrides updateServerStatusIconAndText()
991 mServerIsValid = false;
992
993 } else {
994 mServerIsValid = true;
995 }
996
997 } else {
998 mServerIsValid = false;
999 }
1000
1001 // refresh UI
1002 showRefreshButton(!mServerIsValid);
1003 showServerStatus();
1004 mOkButton.setEnabled(mServerIsValid);
1005
1006 /// very special case (TODO: move to a common place for all the remote operations)
1007 if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {
1008 showUntrustedCertDialog(result);
1009 }
1010 }
1011
1012
1013 private boolean authSupported(AuthenticationMethod authMethod) {
1014 String basic = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType());
1015 String oAuth = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType());
1016 String saml = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType());
1017
1018 return (( mAuthTokenType.equals(basic) &&
1019 authMethod.equals(AuthenticationMethod.BASIC_HTTP_AUTH) ) ||
1020 ( mAuthTokenType.equals(oAuth) &&
1021 authMethod.equals(AuthenticationMethod.BEARER_TOKEN)) ||
1022 ( mAuthTokenType.equals(saml) &&
1023 authMethod.equals(AuthenticationMethod.SAML_WEB_SSO))
1024 );
1025 }
1026
1027
1028 // TODO remove, if possible
1029 private String normalizeUrl(String url) {
1030 if (url != null && url.length() > 0) {
1031 url = url.trim();
1032 if (!url.toLowerCase().startsWith("http://") &&
1033 !url.toLowerCase().startsWith("https://")) {
1034 if (mIsSslConn) {
1035 url = "https://" + url;
1036 } else {
1037 url = "http://" + url;
1038 }
1039 }
1040
1041 url = trimUrlWebdav(url);
1042
1043 if (url.endsWith("/")) {
1044 url = url.substring(0, url.length() - 1);
1045 }
1046
1047 }
1048 return (url != null ? url : "");
1049 }
1050
1051
1052 // TODO remove, if possible
1053 private String trimUrlWebdav(String url){
1054 if(url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_4_0)){
1055 url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_4_0.length());
1056 } else if(url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_2_0)){
1057 url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_2_0.length());
1058 } else if (url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_1_2)){
1059 url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_1_2.length());
1060 }
1061 return (url != null ? url : "");
1062 }
1063
1064
1065 /**
1066 * Chooses the right icon and text to show to the user for the received operation result.
1067 *
1068 * @param result Result of a remote operation performed in this activity
1069 */
1070 private void updateServerStatusIconAndText(RemoteOperationResult result) {
1071 mServerStatusIcon = R.drawable.common_error; // the most common case in the switch below
1072
1073 switch (result.getCode()) {
1074 case OK_SSL:
1075 mServerStatusIcon = android.R.drawable.ic_secure;
1076 mServerStatusText = R.string.auth_secure_connection;
1077 break;
1078
1079 case OK_NO_SSL:
1080 case OK:
1081 if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {
1082 mServerStatusText = R.string.auth_connection_established;
1083 mServerStatusIcon = R.drawable.ic_ok;
1084 } else {
1085 mServerStatusText = R.string.auth_nossl_plain_ok_title;
1086 mServerStatusIcon = android.R.drawable.ic_partial_secure;
1087 }
1088 break;
1089
1090 case NO_NETWORK_CONNECTION:
1091 mServerStatusIcon = R.drawable.no_network;
1092 mServerStatusText = R.string.auth_no_net_conn_title;
1093 break;
1094
1095 case SSL_RECOVERABLE_PEER_UNVERIFIED:
1096 mServerStatusText = R.string.auth_ssl_unverified_server_title;
1097 break;
1098 case BAD_OC_VERSION:
1099 mServerStatusText = R.string.auth_bad_oc_version_title;
1100 break;
1101 case WRONG_CONNECTION:
1102 mServerStatusText = R.string.auth_wrong_connection_title;
1103 break;
1104 case TIMEOUT:
1105 mServerStatusText = R.string.auth_timeout_title;
1106 break;
1107 case INCORRECT_ADDRESS:
1108 mServerStatusText = R.string.auth_incorrect_address_title;
1109 break;
1110 case SSL_ERROR:
1111 mServerStatusText = R.string.auth_ssl_general_error_title;
1112 break;
1113 case UNAUTHORIZED:
1114 mServerStatusText = R.string.auth_unauthorized;
1115 break;
1116 case HOST_NOT_AVAILABLE:
1117 mServerStatusText = R.string.auth_unknown_host_title;
1118 break;
1119 case INSTANCE_NOT_CONFIGURED:
1120 mServerStatusText = R.string.auth_not_configured_title;
1121 break;
1122 case FILE_NOT_FOUND:
1123 mServerStatusText = R.string.auth_incorrect_path_title;
1124 break;
1125 case OAUTH2_ERROR:
1126 mServerStatusText = R.string.auth_oauth_error;
1127 break;
1128 case OAUTH2_ERROR_ACCESS_DENIED:
1129 mServerStatusText = R.string.auth_oauth_error_access_denied;
1130 break;
1131 case UNHANDLED_HTTP_CODE:
1132 case UNKNOWN_ERROR:
1133 mServerStatusText = R.string.auth_unknown_error_title;
1134 break;
1135 default:
1136 mServerStatusText = 0;
1137 mServerStatusIcon = 0;
1138 }
1139 }
1140
1141
1142 /**
1143 * Chooses the right icon and text to show to the user for the received operation result.
1144 *
1145 * @param result Result of a remote operation performed in this activity
1146 */
1147 private void updateAuthStatusIconAndText(RemoteOperationResult result) {
1148 mAuthStatusIcon = R.drawable.common_error; // the most common case in the switch below
1149
1150 switch (result.getCode()) {
1151 case OK_SSL:
1152 mAuthStatusIcon = android.R.drawable.ic_secure;
1153 mAuthStatusText = R.string.auth_secure_connection;
1154 break;
1155
1156 case OK_NO_SSL:
1157 case OK:
1158 if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {
1159 mAuthStatusText = R.string.auth_connection_established;
1160 mAuthStatusIcon = R.drawable.ic_ok;
1161 } else {
1162 mAuthStatusText = R.string.auth_nossl_plain_ok_title;
1163 mAuthStatusIcon = android.R.drawable.ic_partial_secure;
1164 }
1165 break;
1166
1167 case NO_NETWORK_CONNECTION:
1168 mAuthStatusIcon = R.drawable.no_network;
1169 mAuthStatusText = R.string.auth_no_net_conn_title;
1170 break;
1171
1172 case SSL_RECOVERABLE_PEER_UNVERIFIED:
1173 mAuthStatusText = R.string.auth_ssl_unverified_server_title;
1174 break;
1175 case BAD_OC_VERSION:
1176 mAuthStatusText = R.string.auth_bad_oc_version_title;
1177 break;
1178 case WRONG_CONNECTION:
1179 mAuthStatusText = R.string.auth_wrong_connection_title;
1180 break;
1181 case TIMEOUT:
1182 mAuthStatusText = R.string.auth_timeout_title;
1183 break;
1184 case INCORRECT_ADDRESS:
1185 mAuthStatusText = R.string.auth_incorrect_address_title;
1186 break;
1187 case SSL_ERROR:
1188 mAuthStatusText = R.string.auth_ssl_general_error_title;
1189 break;
1190 case UNAUTHORIZED:
1191 mAuthStatusText = R.string.auth_unauthorized;
1192 break;
1193 case HOST_NOT_AVAILABLE:
1194 mAuthStatusText = R.string.auth_unknown_host_title;
1195 break;
1196 case INSTANCE_NOT_CONFIGURED:
1197 mAuthStatusText = R.string.auth_not_configured_title;
1198 break;
1199 case FILE_NOT_FOUND:
1200 mAuthStatusText = R.string.auth_incorrect_path_title;
1201 break;
1202 case OAUTH2_ERROR:
1203 mAuthStatusText = R.string.auth_oauth_error;
1204 break;
1205 case OAUTH2_ERROR_ACCESS_DENIED:
1206 mAuthStatusText = R.string.auth_oauth_error_access_denied;
1207 break;
1208 case ACCOUNT_NOT_NEW:
1209 mAuthStatusText = R.string.auth_account_not_new;
1210 break;
1211 case ACCOUNT_NOT_THE_SAME:
1212 mAuthStatusText = R.string.auth_account_not_the_same;
1213 break;
1214 case UNHANDLED_HTTP_CODE:
1215 case UNKNOWN_ERROR:
1216 mAuthStatusText = R.string.auth_unknown_error_title;
1217 break;
1218 default:
1219 mAuthStatusText = 0;
1220 mAuthStatusIcon = 0;
1221 }
1222 }
1223
1224
1225 private void updateStatusIconFailUserName(){
1226 mAuthStatusIcon = R.drawable.common_error;
1227 mAuthStatusText = R.string.auth_fail_get_user_name;
1228 }
1229
1230 private void updateServerStatusIconNoRegularAuth(){
1231 mServerStatusIcon = R.drawable.common_error;
1232 mServerStatusText = R.string.auth_can_not_auth_against_server;
1233 }
1234
1235 /**
1236 * Processes the result of the request for and access token send
1237 * to an OAuth authorization server.
1238 *
1239 * @param result Result of the operation.
1240 */
1241 private void onGetOAuthAccessTokenFinish(RemoteOperationResult result) {
1242 mOauth2GetAccessTokenOpId = -1;
1243 dismissDialog(WAIT_DIALOG_TAG);
1244
1245 String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);
1246 if (result.isSuccess() && webdav_path != null) {
1247 /// be gentle with the user
1248 IndeterminateProgressDialog dialog =
1249 IndeterminateProgressDialog.newInstance(R.string.auth_trying_to_login, true);
1250 dialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
1251
1252 /// time to test the retrieved access token on the ownCloud server
1253 @SuppressWarnings("unchecked")
1254 Map<String, String> tokens = (Map<String, String>)(result.getData().get(0));
1255 mAuthToken = tokens.get(OAuth2Constants.KEY_ACCESS_TOKEN);
1256 //mAuthToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Constants.KEY_ACCESS_TOKEN);
1257 Log_OC.d(TAG, "Got ACCESS TOKEN: " + mAuthToken);
1258
1259 String remotePath ="";
1260 boolean successIfAbsent = false;
1261 boolean followRedirects = true;
1262 startExistenceCheckRemoteOperation(remotePath, this, successIfAbsent, webdav_path, "", "", followRedirects);
1263
1264 } else {
1265 updateAuthStatusIconAndText(result);
1266 showAuthStatus();
1267 Log_OC.d(TAG, "Access failed: " + result.getLogMessage());
1268 }
1269 }
1270
1271
1272 /**
1273 * Processes the result of the access check performed to try the user credentials.
1274 *
1275 * Creates a new account through the AccountManager.
1276 *
1277 * @param operation Access check performed.
1278 * @param result Result of the operation.
1279 */
1280 private void onAuthorizationCheckFinish(RemoteOperationResult result) {
1281 mExistenceCheckOpId = -1;
1282 dismissDialog(WAIT_DIALOG_TAG);
1283
1284 if (result.isSuccess()) {
1285 Log_OC.d(TAG, "Successful access - time to save the account");
1286
1287 boolean success = false;
1288 if (mAction == ACTION_CREATE) {
1289 success = createAccount();
1290
1291 } else {
1292 updateToken();
1293 success = true;
1294 }
1295
1296 if (success) {
1297 finish();
1298 }
1299
1300 } else if (result.isServerFail() || result.isException()) {
1301 /// if server fail or exception in authorization, the UI is updated as when a server check failed
1302 mServerIsChecked = true;
1303 mServerIsValid = false;
1304 mIsSslConn = false;
1305 mServerInfoOperation = null;
1306 mDiscoveredVersion = null;
1307 mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString());
1308
1309 // update status icon and text
1310 updateServerStatusIconAndText(result);
1311 showServerStatus();
1312 mAuthStatusIcon = 0;
1313 mAuthStatusText = 0;
1314 showAuthStatus();
1315
1316 // update input controls state
1317 showRefreshButton(true);
1318 mOkButton.setEnabled(false);
1319
1320 // very special case (TODO: move to a common place for all the remote operations) (dangerous here?)
1321 if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {
1322 showUntrustedCertDialog(result);
1323 }
1324
1325 } else { // authorization fail due to client side - probably wrong credentials
1326 updateAuthStatusIconAndText(result);
1327 showAuthStatus();
1328 Log_OC.d(TAG, "Access failed: " + result.getLogMessage());
1329 }
1330 }
1331
1332
1333
1334
1335 /**
1336 * Sets the proper response to get that the Account Authenticator that started this activity saves
1337 * a new authorization token for mAccount.
1338 */
1339 private void updateToken() {
1340 Bundle response = new Bundle();
1341 response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);
1342 response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);
1343
1344 if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)) {
1345 response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);
1346 // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention
1347 mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);
1348
1349 } else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) {
1350
1351 response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);
1352 // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention
1353 mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);
1354
1355 } else {
1356 response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());
1357 mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());
1358 }
1359 setAccountAuthenticatorResult(response);
1360
1361 }
1362
1363
1364 /**
1365 * Creates a new account through the Account Authenticator that started this activity.
1366 *
1367 * This makes the account permanent.
1368 *
1369 * TODO Decide how to name the OAuth accounts
1370 */
1371 private boolean createAccount() {
1372 /// create and save new ownCloud account
1373 boolean isOAuth = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType);
1374 boolean isSaml = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType);
1375
1376 Uri uri = Uri.parse(mHostBaseUrl);
1377 String username = mUsernameInput.getText().toString().trim();
1378 if (isOAuth) {
1379 username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();
1380 }
1381 String accountName = username + "@" + uri.getHost();
1382 if (uri.getPort() >= 0) {
1383 accountName += ":" + uri.getPort();
1384 }
1385 mAccount = new Account(accountName, MainApp.getAccountType());
1386 if (AccountUtils.exists(mAccount, getApplicationContext())) {
1387 // fail - not a new account, but an existing one; disallow
1388 RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_NEW);
1389 updateAuthStatusIconAndText(result);
1390 showAuthStatus();
1391 Log_OC.d(TAG, result.getLogMessage());
1392 return false;
1393
1394 } else {
1395
1396 if (isOAuth || isSaml) {
1397 mAccountMgr.addAccountExplicitly(mAccount, "", null); // with external authorizations, the password is never input in the app
1398 } else {
1399 mAccountMgr.addAccountExplicitly(mAccount, mPasswordInput.getText().toString(), null);
1400 }
1401
1402 /// add the new account as default in preferences, if there is none already
1403 Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);
1404 if (defaultAccount == null) {
1405 SharedPreferences.Editor editor = PreferenceManager
1406 .getDefaultSharedPreferences(this).edit();
1407 editor.putString("select_oc_account", accountName);
1408 editor.commit();
1409 }
1410
1411 /// prepare result to return to the Authenticator
1412 // TODO check again what the Authenticator makes with it; probably has the same effect as addAccountExplicitly, but it's not well done
1413 final Intent intent = new Intent();
1414 intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, MainApp.getAccountType());
1415 intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);
1416 /*if (!isOAuth)
1417 intent.putExtra(AccountManager.KEY_AUTHTOKEN, MainApp.getAccountType()); */
1418 intent.putExtra(AccountManager.KEY_USERDATA, username);
1419 if (isOAuth || isSaml) {
1420 mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);
1421 }
1422 /// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA
1423 mAccountMgr.setUserData(mAccount, Constants.KEY_OC_VERSION, mDiscoveredVersion.getVersion());
1424 mAccountMgr.setUserData(mAccount, Constants.KEY_OC_BASE_URL, mHostBaseUrl);
1425
1426 if (isSaml) {
1427 mAccountMgr.setUserData(mAccount, Constants.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE");
1428 } else if (isOAuth) {
1429 mAccountMgr.setUserData(mAccount, Constants.KEY_SUPPORTS_OAUTH2, "TRUE");
1430 }
1431
1432 setAccountAuthenticatorResult(intent.getExtras());
1433 setResult(RESULT_OK, intent);
1434
1435 return true;
1436 }
1437 }
1438
1439
1440 /**
1441 * Starts and activity to open the 'new account' page in the ownCloud web site
1442 *
1443 * @param view 'Account register' button
1444 */
1445 public void onRegisterClick(View view) {
1446 Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.welcome_link_url)));
1447 setResult(RESULT_CANCELED);
1448 startActivity(register);
1449 }
1450
1451
1452 /**
1453 * Updates the content and visibility state of the icon and text associated
1454 * to the last check on the ownCloud server.
1455 */
1456 private void showServerStatus() {
1457 TextView tv = (TextView) findViewById(R.id.server_status_text);
1458
1459 if (mServerStatusIcon == 0 && mServerStatusText == 0) {
1460 tv.setVisibility(View.INVISIBLE);
1461
1462 } else {
1463 tv.setText(mServerStatusText);
1464 tv.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);
1465 tv.setVisibility(View.VISIBLE);
1466 }
1467
1468 }
1469
1470
1471 /**
1472 * Updates the content and visibility state of the icon and text associated
1473 * to the interactions with the OAuth authorization server.
1474 */
1475 private void showAuthStatus() {
1476 if (mAuthStatusIcon == 0 && mAuthStatusText == 0) {
1477 mAuthStatusLayout.setVisibility(View.INVISIBLE);
1478
1479 } else {
1480 mAuthStatusLayout.setText(mAuthStatusText);
1481 mAuthStatusLayout.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);
1482 mAuthStatusLayout.setVisibility(View.VISIBLE);
1483 }
1484 }
1485
1486
1487 private void showRefreshButton (boolean show) {
1488 if (show) {
1489 mRefreshButton.setVisibility(View.VISIBLE);
1490 } else {
1491 mRefreshButton.setVisibility(View.GONE);
1492 }
1493 }
1494
1495 /**
1496 * Called when the refresh button in the input field for ownCloud host is clicked.
1497 *
1498 * Performs a new check on the URL in the input field.
1499 *
1500 * @param view Refresh 'button'
1501 */
1502 public void onRefreshClick(View view) {
1503 checkOcServer();
1504 }
1505
1506
1507 /**
1508 * Called when the eye icon in the password field is clicked.
1509 *
1510 * Toggles the visibility of the password in the field.
1511 */
1512 public void onViewPasswordClick() {
1513 int selectionStart = mPasswordInput.getSelectionStart();
1514 int selectionEnd = mPasswordInput.getSelectionEnd();
1515 if (isPasswordVisible()) {
1516 hidePassword();
1517 } else {
1518 showPassword();
1519 }
1520 mPasswordInput.setSelection(selectionStart, selectionEnd);
1521 }
1522
1523
1524 /**
1525 * Called when the checkbox for OAuth authorization is clicked.
1526 *
1527 * Hides or shows the input fields for user & password.
1528 *
1529 * @param view 'View password' 'button'
1530 */
1531 public void onCheckClick(View view) {
1532 CheckBox oAuth2Check = (CheckBox)view;
1533 if (oAuth2Check.isChecked()) {
1534 mAuthTokenType = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType());
1535 } else {
1536 mAuthTokenType = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType());
1537 }
1538 adaptViewAccordingToAuthenticationMethod();
1539 }
1540
1541
1542 /**
1543 * Changes the visibility of input elements depending on
1544 * the current authorization method.
1545 */
1546 private void adaptViewAccordingToAuthenticationMethod () {
1547 if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)) {
1548 // OAuth 2 authorization
1549 mOAuthAuthEndpointText.setVisibility(View.VISIBLE);
1550 mOAuthTokenEndpointText.setVisibility(View.VISIBLE);
1551 mUsernameInput.setVisibility(View.GONE);
1552 mPasswordInput.setVisibility(View.GONE);
1553
1554 } else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) {
1555 // SAML-based web Single Sign On
1556 mOAuthAuthEndpointText.setVisibility(View.GONE);
1557 mOAuthTokenEndpointText.setVisibility(View.GONE);
1558 mUsernameInput.setVisibility(View.GONE);
1559 mPasswordInput.setVisibility(View.GONE);
1560 } else {
1561 // basic HTTP authorization
1562 mOAuthAuthEndpointText.setVisibility(View.GONE);
1563 mOAuthTokenEndpointText.setVisibility(View.GONE);
1564 mUsernameInput.setVisibility(View.VISIBLE);
1565 mPasswordInput.setVisibility(View.VISIBLE);
1566 }
1567 }
1568
1569 /**
1570 * Called when the 'action' button in an IME is pressed ('enter' in software keyboard).
1571 *
1572 * Used to trigger the authentication check when the user presses 'enter' after writing the password,
1573 * or to throw the server test when the only field on screen is the URL input field.
1574 */
1575 @Override
1576 public boolean onEditorAction(TextView inputField, int actionId, KeyEvent event) {
1577 if (actionId == EditorInfo.IME_ACTION_DONE && inputField != null && inputField.equals(mPasswordInput)) {
1578 if (mOkButton.isEnabled()) {
1579 mOkButton.performClick();
1580 }
1581
1582 } else if (actionId == EditorInfo.IME_ACTION_NEXT && inputField != null && inputField.equals(mHostUrlInput)) {
1583 if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) {
1584 checkOcServer();
1585 }
1586 }
1587 return false; // always return false to grant that the software keyboard is hidden anyway
1588 }
1589
1590
1591 private abstract static class RightDrawableOnTouchListener implements OnTouchListener {
1592
1593 private int fuzz = 75;
1594
1595 /**
1596 * {@inheritDoc}
1597 */
1598 @Override
1599 public boolean onTouch(View view, MotionEvent event) {
1600 Drawable rightDrawable = null;
1601 if (view instanceof TextView) {
1602 Drawable[] drawables = ((TextView)view).getCompoundDrawables();
1603 if (drawables.length > 2) {
1604 rightDrawable = drawables[2];
1605 }
1606 }
1607 if (rightDrawable != null) {
1608 final int x = (int) event.getX();
1609 final int y = (int) event.getY();
1610 final Rect bounds = rightDrawable.getBounds();
1611 if (x >= (view.getRight() - bounds.width() - fuzz) && x <= (view.getRight() - view.getPaddingRight() + fuzz)
1612 && y >= (view.getPaddingTop() - fuzz) && y <= (view.getHeight() - view.getPaddingBottom()) + fuzz) {
1613
1614 return onDrawableTouch(event);
1615 }
1616 }
1617 return false;
1618 }
1619
1620 public abstract boolean onDrawableTouch(final MotionEvent event);
1621 }
1622
1623
1624 public void onSamlDialogSuccess(String sessionCookie) {
1625 mAuthToken = sessionCookie;
1626
1627 if (sessionCookie != null && sessionCookie.length() > 0) {
1628 mAuthToken = sessionCookie;
1629 boolean followRedirects = true;
1630 getRemoteUserNameOperation(sessionCookie, followRedirects);
1631 }
1632 }
1633
1634 private void getRemoteUserNameOperation(String sessionCookie, boolean followRedirects) {
1635
1636 Intent getUserNameIntent = new Intent();
1637 getUserNameIntent.setAction(OperationsService.ACTION_GET_USER_NAME);
1638 getUserNameIntent.putExtra(OperationsService.EXTRA_SERVER_URL, mHostBaseUrl);
1639 getUserNameIntent.putExtra(OperationsService.EXTRA_COOKIE, sessionCookie);
1640 getUserNameIntent.putExtra(OperationsService.EXTRA_FOLLOW_REDIRECTS, followRedirects);
1641
1642 if (mOperationsServiceBinder != null) {
1643 //Log_OC.wtf(TAG, "starting getRemoteUserNameOperation..." );
1644 mGetUserNameOpId = mOperationsServiceBinder.newOperation(getUserNameIntent);
1645 }
1646 }
1647
1648
1649 @Override
1650 public void onSsoFinished(String sessionCookies) {
1651 if (sessionCookies != null && sessionCookies.length() > 0) {
1652 Log_OC.d(TAG, "Successful SSO - time to save the account");
1653 onSamlDialogSuccess(sessionCookies);
1654 Fragment fd = getSupportFragmentManager().findFragmentByTag(SAML_DIALOG_TAG);
1655 if (fd != null && fd instanceof SherlockDialogFragment) {
1656 Dialog d = ((SherlockDialogFragment)fd).getDialog();
1657 if (d != null && d.isShowing()) {
1658 d.dismiss();
1659 }
1660 }
1661
1662 } else {
1663 // TODO - show fail
1664 Log_OC.d(TAG, "SSO failed");
1665 }
1666
1667 }
1668
1669 /** Show auth_message
1670 *
1671 * @param message
1672 */
1673 private void showAuthMessage(String message) {
1674 mAuthMessage.setVisibility(View.VISIBLE);
1675 mAuthMessage.setText(message);
1676 }
1677
1678 private void hideAuthMessage() {
1679 mAuthMessage.setVisibility(View.GONE);
1680 }
1681
1682 @Override
1683 public boolean onTouchEvent(MotionEvent event) {
1684 if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType) &&
1685 mHostUrlInput.hasFocus() && event.getAction() == MotionEvent.ACTION_DOWN) {
1686 checkOcServer();
1687 }
1688 return super.onTouchEvent(event);
1689 }
1690
1691
1692 /**
1693 * Show untrusted cert dialog
1694 */
1695 public void showUntrustedCertDialog(X509Certificate x509Certificate, SslError error, SslErrorHandler handler) {
1696 // Show a dialog with the certificate info
1697 SslUntrustedCertDialog dialog = null;
1698 if (x509Certificate == null) {
1699 dialog = SslUntrustedCertDialog.newInstanceForEmptySslError(error, handler);
1700 } else {
1701 dialog = SslUntrustedCertDialog.newInstanceForFullSslError(x509Certificate, error, handler);
1702 }
1703 FragmentManager fm = getSupportFragmentManager();
1704 FragmentTransaction ft = fm.beginTransaction();
1705 ft.addToBackStack(null);
1706 dialog.show(ft, DIALOG_UNTRUSTED_CERT);
1707 }
1708
1709 /**
1710 * Show untrusted cert dialog
1711 */
1712 public void showUntrustedCertDialog(RemoteOperationResult result) {
1713 // Show a dialog with the certificate info
1714 SslUntrustedCertDialog dialog = SslUntrustedCertDialog.newInstanceForFullSslError((CertificateCombinedException)result.getException());
1715 FragmentManager fm = getSupportFragmentManager();
1716 FragmentTransaction ft = fm.beginTransaction();
1717 ft.addToBackStack(null);
1718 dialog.show(ft, DIALOG_UNTRUSTED_CERT);
1719
1720 }
1721
1722 /**
1723 * Called from SslValidatorDialog when a new server certificate was correctly saved.
1724 */
1725 public void onSavedCertificate() {
1726 Fragment fd = getSupportFragmentManager().findFragmentByTag(SAML_DIALOG_TAG);
1727 if (fd == null) {
1728 // if SAML dialog is not shown, the SslDialog was shown due to an SSL error in the server check
1729 checkOcServer();
1730 }
1731 }
1732
1733 /**
1734 * Called from SslValidatorDialog when a new server certificate could not be saved
1735 * when the user requested it.
1736 */
1737 @Override
1738 public void onFailedSavingCertificate() {
1739 dismissDialog(SAML_DIALOG_TAG);
1740 Toast.makeText(this, R.string.ssl_validator_not_saved, Toast.LENGTH_LONG).show();
1741 }
1742
1743 @Override
1744 public void onCancelCertificate() {
1745 dismissDialog(SAML_DIALOG_TAG);
1746 }
1747
1748
1749 private void doOnResumeAndBound() {
1750 //Log.wtf(TAG, "registering to listen for operation callbacks" );
1751 mOperationsServiceBinder.addOperationListener(AuthenticatorActivity.this, mHandler);
1752
1753 if (mGetServerInfoOpId != -1) {
1754 RemoteOperationResult result =
1755 mOperationsServiceBinder.getOperationResultIfFinished(mGetServerInfoOpId);
1756 if (result != null) {
1757 //Log_OC.wtf(TAG, "found result of operation finished while rotating");
1758 onGetServerInfoFinish(result);
1759 }
1760
1761 } else if (mOauth2GetAccessTokenOpId != -1) {
1762 RemoteOperationResult result =
1763 mOperationsServiceBinder.getOperationResultIfFinished(
1764 mOauth2GetAccessTokenOpId);
1765 if (result != null) {
1766 //Log_OC.wtf(TAG, "found result of operation finished while rotating");
1767 onGetOAuthAccessTokenFinish(result);
1768 }
1769
1770 } else if (mExistenceCheckOpId != -1) {
1771 RemoteOperationResult result =
1772 mOperationsServiceBinder.getOperationResultIfFinished(mExistenceCheckOpId);
1773 if (result != null) {
1774 //Log_OC.wtf(TAG, "found result of operation finished while rotating");
1775 if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(
1776 MainApp.getAccountType()).equals(mAuthTokenType)) {
1777 onSamlBasedFederatedSingleSignOnAuthorizationStart(result);
1778
1779 } else {
1780 onAuthorizationCheckFinish(result);
1781 }
1782 }
1783 }if (mGetUserNameOpId != -1) {
1784 RemoteOperationResult result =
1785 mOperationsServiceBinder.getOperationResultIfFinished(mGetUserNameOpId);
1786 if (result != null) {
1787 //Log_OC.wtf(TAG, "found result of operation finished while rotating");
1788 onGetUserNameFinish(result);
1789 }
1790
1791 }
1792
1793 }
1794
1795
1796 private void dismissDialog(String dialogTag){
1797 Fragment frag = getSupportFragmentManager().findFragmentByTag(dialogTag);
1798 if (frag != null && frag instanceof SherlockDialogFragment) {
1799 SherlockDialogFragment dialog = (SherlockDialogFragment) frag;
1800 dialog.dismiss();
1801 }
1802 }
1803
1804
1805 /**
1806 * Implements callback methods for service binding.
1807 */
1808 private class OperationsServiceConnection implements ServiceConnection {
1809
1810 @Override
1811 public void onServiceConnected(ComponentName component, IBinder service) {
1812 if (component.equals(new ComponentName(AuthenticatorActivity.this, OperationsService.class))) {
1813 //Log_OC.wtf(TAG, "Operations service connected");
1814 mOperationsServiceBinder = (OperationsServiceBinder) service;
1815
1816 doOnResumeAndBound();
1817
1818 } else {
1819 return;
1820 }
1821
1822 }
1823
1824 @Override
1825 public void onServiceDisconnected(ComponentName component) {
1826 if (component.equals(new ComponentName(AuthenticatorActivity.this, OperationsService.class))) {
1827 Log_OC.e(TAG, "Operations service crashed");
1828 mOperationsServiceBinder = null;
1829 }
1830 }
1831
1832 }
1833
1834 }