Redirect app to login screen when operations in file details view fail due to bad...
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / AuthenticatorActivity.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
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.ui.activity;
20
21 import java.net.MalformedURLException;
22 import java.net.URL;
23 import java.util.HashMap;
24 import java.util.Map;
25
26 import org.json.JSONException;
27 import org.json.JSONObject;
28
29 import com.owncloud.android.AccountUtils;
30 import com.owncloud.android.authenticator.AccountAuthenticator;
31 import com.owncloud.android.authenticator.AuthenticationRunnable;
32 import com.owncloud.android.authenticator.OnAuthenticationResultListener;
33 import com.owncloud.android.authenticator.OnConnectCheckListener;
34 import com.owncloud.android.authenticator.oauth2.OAuth2Context;
35 import com.owncloud.android.authenticator.oauth2.OAuth2GetCodeRunnable;
36 import com.owncloud.android.authenticator.oauth2.OnOAuth2GetCodeResultListener;
37 import com.owncloud.android.authenticator.oauth2.connection.ConnectorOAuth2;
38 import com.owncloud.android.authenticator.oauth2.services.OAuth2GetTokenService;
39 import com.owncloud.android.ui.dialog.SslValidatorDialog;
40 import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
41 import com.owncloud.android.utils.OwnCloudVersion;
42 import com.owncloud.android.network.OwnCloudClientUtils;
43 import com.owncloud.android.operations.ConnectionCheckOperation;
44 import com.owncloud.android.operations.ExistenceCheckOperation;
45 import com.owncloud.android.operations.GetOAuth2AccessToken;
46 import com.owncloud.android.operations.OnRemoteOperationListener;
47 import com.owncloud.android.operations.RemoteOperation;
48 import com.owncloud.android.operations.RemoteOperationResult;
49
50 import android.accounts.Account;
51 import android.accounts.AccountAuthenticatorActivity;
52 import android.accounts.AccountManager;
53 import android.app.AlertDialog;
54 import android.app.Dialog;
55 import android.app.ProgressDialog;
56 import android.content.BroadcastReceiver;
57 import android.content.ContentResolver;
58 import android.content.Context;
59 import android.content.DialogInterface;
60 import android.content.Intent;
61 import android.content.IntentFilter;
62 import android.content.SharedPreferences;
63 import android.net.Uri;
64 import android.os.Bundle;
65 import android.os.Handler;
66 import android.preference.PreferenceManager;
67 import android.text.InputType;
68 import android.util.Log;
69 import android.view.View;
70 import android.view.View.OnClickListener;
71 import android.view.View.OnFocusChangeListener;
72 import android.view.Window;
73 import android.widget.CheckBox;
74 import android.widget.EditText;
75 import android.widget.Button;
76 import android.widget.ImageView;
77 import android.widget.TextView;
78 import com.owncloud.android.R;
79
80 import eu.alefzero.webdav.WebdavClient;
81
82 /**
83 * This Activity is used to add an ownCloud account to the App
84 *
85 * @author Bartek Przybylski
86 *
87 */
88 public class AuthenticatorActivity extends AccountAuthenticatorActivity
89 implements OnAuthenticationResultListener, OnConnectCheckListener, OnRemoteOperationListener, OnSslValidatorListener,
90 OnFocusChangeListener, OnClickListener, OnOAuth2GetCodeResultListener {
91
92 private static final int DIALOG_LOGIN_PROGRESS = 0;
93 private static final int DIALOG_SSL_VALIDATOR = 1;
94 private static final int DIALOG_CERT_NOT_SAVED = 2;
95
96 private static final String TAG = "AuthActivity";
97
98 private Thread mAuthThread;
99 private AuthenticationRunnable mAuthRunnable;
100 private ConnectionCheckOperation mConnChkRunnable;
101 private ExistenceCheckOperation mAuthChkOperation;
102 private final Handler mHandler = new Handler();
103 private String mBaseUrl;
104 private OwnCloudVersion mDiscoveredVersion;
105
106 private static final String STATUS_TEXT = "STATUS_TEXT";
107 private static final String STATUS_ICON = "STATUS_ICON";
108 private static final String STATUS_CORRECT = "STATUS_CORRECT";
109 private static final String IS_SSL_CONN = "IS_SSL_CONN";
110 private static final String OC_VERSION = "OC_VERSION";
111 private int mStatusText, mStatusIcon;
112 private boolean mStatusCorrect, mIsSslConn;
113 private RemoteOperationResult mLastSslUntrustedServerResult;
114
115 public static final String PARAM_ACCOUNTNAME = "param_Accountname";
116
117 public static final String PARAM_USERNAME = "param_Username";
118 public static final String PARAM_HOSTNAME = "param_Hostname";
119
120 // oAuth2 variables.
121 private static final int OAUTH2_LOGIN_PROGRESS = 3;
122 private static final String OAUTH2_STATUS_TEXT = "OAUTH2_STATUS_TEXT";
123 private static final String OAUTH2_STATUS_ICON = "OAUTH2_STATUS_ICON";
124 private static final String OAUTH2_CODE_RESULT = "CODE_RESULT";
125 private static final String OAUTH2_IS_CHECKED = "OAUTH2_IS_CHECKED";
126 private Thread mOAuth2GetCodeThread;
127 private OAuth2GetCodeRunnable mOAuth2GetCodeRunnable;
128 private TokenReceiver tokenReceiver;
129 private JSONObject codeResponseJson;
130 private int mOAuth2StatusText, mOAuth2StatusIcon;
131
132 public ConnectorOAuth2 connectorOAuth2;
133
134 // Variables used to save the on the state the contents of all fields.
135 private static final String HOST_URL_TEXT = "HOST_URL_TEXT";
136 private static final String ACCOUNT_USERNAME = "ACCOUNT_USERNAME";
137 private static final String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD";
138
139 //private boolean mNewRedirectUriCaptured;
140 private Uri mNewCapturedUriFromOAuth2Redirection;
141
142 // END of oAuth2 variables.
143
144 @Override
145 protected void onCreate(Bundle savedInstanceState) {
146 super.onCreate(savedInstanceState);
147 getWindow().requestFeature(Window.FEATURE_NO_TITLE);
148 setContentView(R.layout.account_setup);
149 ImageView iv = (ImageView) findViewById(R.id.refreshButton);
150 ImageView iv2 = (ImageView) findViewById(R.id.viewPassword);
151 TextView tv = (TextView) findViewById(R.id.host_URL);
152 TextView tv2 = (TextView) findViewById(R.id.account_password);
153 EditText oauth2Url = (EditText)findViewById(R.id.oAuth_URL);
154 oauth2Url.setText("OWNCLOUD AUTHORIZATION PROVIDER IN TEST");
155
156 if (savedInstanceState != null) {
157 mStatusIcon = savedInstanceState.getInt(STATUS_ICON);
158 mStatusText = savedInstanceState.getInt(STATUS_TEXT);
159 mStatusCorrect = savedInstanceState.getBoolean(STATUS_CORRECT);
160 mIsSslConn = savedInstanceState.getBoolean(IS_SSL_CONN);
161 setResultIconAndText(mStatusIcon, mStatusText);
162 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
163 if (!mStatusCorrect)
164 iv.setVisibility(View.VISIBLE);
165 else
166 iv.setVisibility(View.INVISIBLE);
167
168 String ocVersion = savedInstanceState.getString(OC_VERSION, null);
169 if (ocVersion != null)
170 mDiscoveredVersion = new OwnCloudVersion(ocVersion);
171
172 // Getting the state of oAuth2 components.
173 mOAuth2StatusIcon = savedInstanceState.getInt(OAUTH2_STATUS_ICON);
174 mOAuth2StatusText = savedInstanceState.getInt(OAUTH2_STATUS_TEXT);
175 // We set this to true if the rotation happens when the user is validating oAuth2 user_code.
176 changeViewByOAuth2Check(savedInstanceState.getBoolean(OAUTH2_IS_CHECKED));
177 // We store a JSon object with all the data returned from oAuth2 server when we get user_code.
178 // Is better than store variable by variable. We use String object to serialize from/to it.
179 try {
180 if (savedInstanceState.containsKey(OAUTH2_CODE_RESULT)) {
181 codeResponseJson = new JSONObject(savedInstanceState.getString(OAUTH2_CODE_RESULT));
182 }
183 } catch (JSONException e) {
184 Log.e(TAG, "onCreate->JSONException: " + e.toString());
185 }
186 // END of getting the state of oAuth2 components.
187
188 // Getting contents of each field.
189 EditText hostUrl = (EditText)findViewById(R.id.host_URL);
190 hostUrl.setText(savedInstanceState.getString(HOST_URL_TEXT), TextView.BufferType.EDITABLE);
191 EditText accountUsername = (EditText)findViewById(R.id.account_username);
192 accountUsername.setText(savedInstanceState.getString(ACCOUNT_USERNAME), TextView.BufferType.EDITABLE);
193 EditText accountPassword = (EditText)findViewById(R.id.account_password);
194 accountPassword.setText(savedInstanceState.getString(ACCOUNT_PASSWORD), TextView.BufferType.EDITABLE);
195 // END of getting contents of each field
196
197 } else {
198 mStatusText = mStatusIcon = 0;
199 mStatusCorrect = false;
200 mIsSslConn = false;
201
202 String accountName = getIntent().getExtras().getString(PARAM_ACCOUNTNAME);
203 String tokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);
204 if (AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN.equals(tokenType)) {
205 CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);
206 oAuth2Check.setChecked(true);
207 changeViewByOAuth2Check(true);
208 }
209
210 if (accountName != null) {
211 ((TextView) findViewById(R.id.account_username)).setText(accountName.substring(0, accountName.lastIndexOf('@')));
212 tv.setText(accountName.substring(accountName.lastIndexOf('@') + 1));
213 }
214 }
215 iv.setOnClickListener(this);
216 iv2.setOnClickListener(this);
217 tv.setOnFocusChangeListener(this);
218 tv2.setOnFocusChangeListener(this);
219
220 Button b = (Button) findViewById(R.id.account_register);
221 if (b != null) {
222 b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name)));
223 }
224
225 mNewCapturedUriFromOAuth2Redirection = null;
226 }
227
228
229 @Override
230 protected void onNewIntent (Intent intent) {
231 Uri data = intent.getData();
232 //mNewRedirectUriCaptured = (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI));
233 if (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI)) {
234 mNewCapturedUriFromOAuth2Redirection = data;
235 }
236 Log.d(TAG, "onNewIntent()");
237
238 }
239
240
241 @Override
242 protected void onSaveInstanceState(Bundle outState) {
243 outState.putInt(STATUS_ICON, mStatusIcon);
244 outState.putInt(STATUS_TEXT, mStatusText);
245 outState.putBoolean(STATUS_CORRECT, mStatusCorrect);
246 if (mDiscoveredVersion != null)
247 outState.putString(OC_VERSION, mDiscoveredVersion.toString());
248
249 // Saving the state of oAuth2 components.
250 outState.putInt(OAUTH2_STATUS_ICON, mOAuth2StatusIcon);
251 outState.putInt(OAUTH2_STATUS_TEXT, mOAuth2StatusText);
252 CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);
253 outState.putBoolean(OAUTH2_IS_CHECKED, oAuth2Check.isChecked());
254 if (codeResponseJson != null){
255 outState.putString(OAUTH2_CODE_RESULT, codeResponseJson.toString());
256 }
257 // END of saving the state of oAuth2 components.
258
259 // Saving contents of each field.
260 outState.putString(HOST_URL_TEXT,((TextView) findViewById(R.id.host_URL)).getText().toString().trim());
261 outState.putString(ACCOUNT_USERNAME,((TextView) findViewById(R.id.account_username)).getText().toString().trim());
262 outState.putString(ACCOUNT_PASSWORD,((TextView) findViewById(R.id.account_password)).getText().toString().trim());
263
264 super.onSaveInstanceState(outState);
265 }
266
267 @Override
268 protected Dialog onCreateDialog(int id) {
269 Dialog dialog = null;
270 switch (id) {
271 case DIALOG_LOGIN_PROGRESS: {
272 ProgressDialog working_dialog = new ProgressDialog(this);
273 working_dialog.setMessage(getResources().getString(
274 R.string.auth_trying_to_login));
275 working_dialog.setIndeterminate(true);
276 working_dialog.setCancelable(true);
277 working_dialog
278 .setOnCancelListener(new DialogInterface.OnCancelListener() {
279 @Override
280 public void onCancel(DialogInterface dialog) {
281 Log.i(TAG, "Login canceled");
282 if (mAuthThread != null) {
283 mAuthThread.interrupt();
284 finish();
285 }
286 }
287 });
288 dialog = working_dialog;
289 break;
290 }
291 // oAuth2 dialog. We show here to the user the URL and user_code that the user must validate in a web browser.
292 case OAUTH2_LOGIN_PROGRESS: {
293 ProgressDialog working_dialog = new ProgressDialog(this);
294 try {
295 if (codeResponseJson != null && codeResponseJson.has(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL)) {
296 working_dialog.setMessage(String.format(getString(R.string.oauth_code_validation_message),
297 codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL),
298 codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE)));
299 } else {
300 working_dialog.setMessage(String.format("Getting authorization"));
301 }
302 } catch (JSONException e) {
303 Log.e(TAG, "onCreateDialog->JSONException: " + e.toString());
304 }
305 working_dialog.setIndeterminate(true);
306 working_dialog.setCancelable(true);
307 working_dialog
308 .setOnCancelListener(new DialogInterface.OnCancelListener() {
309 @Override
310 public void onCancel(DialogInterface dialog) {
311 Log.i(TAG, "Login canceled");
312 if (mOAuth2GetCodeThread != null) {
313 mOAuth2GetCodeThread.interrupt();
314 finish();
315 }
316 if (tokenReceiver != null) {
317 unregisterReceiver(tokenReceiver);
318 tokenReceiver = null;
319 finish();
320 }
321 }
322 });
323 dialog = working_dialog;
324 break;
325 }
326 case DIALOG_SSL_VALIDATOR: {
327 dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
328 break;
329 }
330 case DIALOG_CERT_NOT_SAVED: {
331 AlertDialog.Builder builder = new AlertDialog.Builder(this);
332 builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
333 builder.setCancelable(false);
334 builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
335 @Override
336 public void onClick(DialogInterface dialog, int which) {
337 dialog.dismiss();
338 };
339 });
340 dialog = builder.create();
341 break;
342 }
343 default:
344 Log.e(TAG, "Incorrect dialog called with id = " + id);
345 }
346 return dialog;
347 }
348
349 @Override
350 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
351 switch (id) {
352 case DIALOG_LOGIN_PROGRESS:
353 case DIALOG_CERT_NOT_SAVED:
354 case OAUTH2_LOGIN_PROGRESS:
355 break;
356 case DIALOG_SSL_VALIDATOR: {
357 ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
358 break;
359 }
360 default:
361 Log.e(TAG, "Incorrect dialog called with id = " + id);
362 }
363 }
364
365 @Override
366 protected void onResume() {
367 Log.d(TAG, "onResume() start");
368 // (old oauth code) Registering token receiver. We must listening to the service that is pooling to the oAuth server for a token.
369 if (tokenReceiver == null) {
370 IntentFilter tokenFilter = new IntentFilter(OAuth2GetTokenService.TOKEN_RECEIVED_MESSAGE);
371 tokenReceiver = new TokenReceiver();
372 this.registerReceiver(tokenReceiver,tokenFilter);
373 }
374 // (new oauth code)
375 /*if (mNewRedirectUriCaptured) {
376 mNewRedirectUriCaptured = false;*/
377 if (mNewCapturedUriFromOAuth2Redirection != null) {
378 getOAuth2AccessTokenFromCapturedRedirection();
379
380 }
381 super.onResume();
382 }
383
384 @Override
385 protected void onPause() {
386 Log.d(TAG, "onPause() start");
387 super.onPause();
388 }
389
390
391 public void onAuthenticationResult(boolean success, String message) {
392 if (success) {
393 TextView username_text = (TextView) findViewById(R.id.account_username), password_text = (TextView) findViewById(R.id.account_password);
394
395 URL url;
396 try {
397 url = new URL(message);
398 } catch (MalformedURLException e) {
399 // should never happen
400 Log.e(getClass().getName(), "Malformed URL: " + message);
401 return;
402 }
403
404 String username = username_text.getText().toString().trim();
405 String accountName = username + "@" + url.getHost();
406 if (url.getPort() >= 0) {
407 accountName += ":" + url.getPort();
408 }
409 Account account = new Account(accountName,
410 AccountAuthenticator.ACCOUNT_TYPE);
411 AccountManager accManager = AccountManager.get(this);
412 accManager.addAccountExplicitly(account, password_text.getText()
413 .toString(), null);
414
415 // Add this account as default in the preferences, if there is none
416 // already
417 Account defaultAccount = AccountUtils
418 .getCurrentOwnCloudAccount(this);
419 if (defaultAccount == null) {
420 SharedPreferences.Editor editor = PreferenceManager
421 .getDefaultSharedPreferences(this).edit();
422 editor.putString("select_oc_account", accountName);
423 editor.commit();
424 }
425
426 final Intent intent = new Intent();
427 intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE,
428 AccountAuthenticator.ACCOUNT_TYPE);
429 intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
430 intent.putExtra(AccountManager.KEY_AUTHTOKEN,
431 AccountAuthenticator.ACCOUNT_TYPE);
432 intent.putExtra(AccountManager.KEY_USERDATA, username);
433
434 accManager.setUserData(account, AccountAuthenticator.KEY_OC_URL,
435 url.toString());
436 accManager.setUserData(account,
437 AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString());
438
439 accManager.setUserData(account,
440 AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);
441
442 setAccountAuthenticatorResult(intent.getExtras());
443 setResult(RESULT_OK, intent);
444 Bundle bundle = new Bundle();
445 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
446 //getContentResolver().startSync(ProviderTableMeta.CONTENT_URI,
447 // bundle);
448 ContentResolver.requestSync(account, "org.owncloud", bundle);
449
450 /*
451 * if
452 * (mConnChkRunnable.getDiscoveredVersion().compareTo(OwnCloudVersion
453 * .owncloud_v2) >= 0) { Intent i = new Intent(this,
454 * ExtensionsAvailableActivity.class); startActivity(i); }
455 */
456
457 finish();
458 } else {
459 try {
460 dismissDialog(DIALOG_LOGIN_PROGRESS);
461 } catch (IllegalArgumentException e) {
462 // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens
463 }
464 TextView tv = (TextView) findViewById(R.id.account_username);
465 tv.setError(message + " "); // the extra spaces are a workaround for an ugly bug:
466 // 1. insert wrong credentials and connect
467 // 2. put the focus on the user name field with using hardware controls (don't touch the screen); the error is shown UNDER the field
468 // 3. touch the user name field; the software keyboard appears; the error popup is moved OVER the field and SHRINKED in width, losing the last word
469 // Seen, at least, in Android 2.x devices
470 }
471 }
472 public void onCancelClick(View view) {
473 setResult(RESULT_CANCELED);
474 finish();
475 }
476
477 public void onOkClick(View view) {
478 String prefix = "";
479 String url = ((TextView) findViewById(R.id.host_URL)).getText()
480 .toString().trim();
481 if (mIsSslConn) {
482 prefix = "https://";
483 } else {
484 prefix = "http://";
485 }
486 if (url.toLowerCase().startsWith("http://")
487 || url.toLowerCase().startsWith("https://")) {
488 prefix = "";
489 }
490 CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);
491 if (oAuth2Check != null && oAuth2Check.isChecked()) {
492 startOauthorization();
493
494 } else {
495 continueConnection(prefix);
496 }
497 }
498
499 private void startOauthorization() {
500 // We start a thread to get an authorization code from the oAuth2 server.
501 setOAuth2ResultIconAndText(R.drawable.progress_small, R.string.oauth_login_connection);
502 mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(OAuth2Context.OAUTH2_F_AUTHORIZATION_ENDPOINT_URL, this);
503 //mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(OAuth2Context.OAUTH2_G_DEVICE_GETCODE_URL, this);
504 mOAuth2GetCodeRunnable.setListener(this, mHandler);
505 mOAuth2GetCodeThread = new Thread(mOAuth2GetCodeRunnable);
506 mOAuth2GetCodeThread.start();
507 }
508
509 public void onRegisterClick(View view) {
510 Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register)));
511 setResult(RESULT_CANCELED);
512 startActivity(register);
513 }
514
515 private void continueConnection(String prefix) {
516 String url = ((TextView) findViewById(R.id.host_URL)).getText()
517 .toString().trim();
518 String username = ((TextView) findViewById(R.id.account_username))
519 .getText().toString();
520 String password = ((TextView) findViewById(R.id.account_password))
521 .getText().toString();
522 if (url.endsWith("/"))
523 url = url.substring(0, url.length() - 1);
524
525 URL uri = null;
526 mDiscoveredVersion = mConnChkRunnable.getDiscoveredVersion();
527 String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, false);
528
529 if (webdav_path == null) {
530 onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title));
531 return;
532 }
533
534 try {
535 mBaseUrl = prefix + url;
536 String url_str = prefix + url + webdav_path;
537 uri = new URL(url_str);
538 } catch (MalformedURLException e) {
539 // should never happen
540 onAuthenticationResult(false, getString(R.string.auth_incorrect_address_title));
541 return;
542 }
543
544 showDialog(DIALOG_LOGIN_PROGRESS);
545 mAuthRunnable = new AuthenticationRunnable(uri, username, password, this);
546 mAuthRunnable.setOnAuthenticationResultListener(this, mHandler);
547 mAuthThread = new Thread(mAuthRunnable);
548 mAuthThread.start();
549 }
550
551 @Override
552 public void onConnectionCheckResult(ResultType type) {
553 mStatusText = mStatusIcon = 0;
554 mStatusCorrect = false;
555 String t_url = ((TextView) findViewById(R.id.host_URL)).getText()
556 .toString().trim().toLowerCase();
557
558 switch (type) {
559 case OK_SSL:
560 mIsSslConn = true;
561 mStatusIcon = android.R.drawable.ic_secure;
562 mStatusText = R.string.auth_secure_connection;
563 mStatusCorrect = true;
564 break;
565 case OK_NO_SSL:
566 mIsSslConn = false;
567 mStatusCorrect = true;
568 if (t_url.startsWith("http://") ) {
569 mStatusText = R.string.auth_connection_established;
570 mStatusIcon = R.drawable.ic_ok;
571 } else {
572 mStatusText = R.string.auth_nossl_plain_ok_title;
573 mStatusIcon = android.R.drawable.ic_partial_secure;
574 }
575 break;
576 case BAD_OC_VERSION:
577 mStatusIcon = R.drawable.common_error;
578 mStatusText = R.string.auth_bad_oc_version_title;
579 break;
580 case WRONG_CONNECTION:
581 mStatusIcon = R.drawable.common_error;
582 mStatusText = R.string.auth_wrong_connection_title;
583 break;
584 case TIMEOUT:
585 mStatusIcon = R.drawable.common_error;
586 mStatusText = R.string.auth_timeout_title;
587 break;
588 case INCORRECT_ADDRESS:
589 mStatusIcon = R.drawable.common_error;
590 mStatusText = R.string.auth_incorrect_address_title;
591 break;
592 case SSL_UNVERIFIED_SERVER:
593 mStatusIcon = R.drawable.common_error;
594 mStatusText = R.string.auth_ssl_unverified_server_title;
595 break;
596 case SSL_INIT_ERROR:
597 mStatusIcon = R.drawable.common_error;
598 mStatusText = R.string.auth_ssl_general_error_title;
599 break;
600 case HOST_NOT_AVAILABLE:
601 mStatusIcon = R.drawable.common_error;
602 mStatusText = R.string.auth_unknown_host_title;
603 break;
604 case NO_NETWORK_CONNECTION:
605 mStatusIcon = R.drawable.no_network;
606 mStatusText = R.string.auth_no_net_conn_title;
607 break;
608 case INSTANCE_NOT_CONFIGURED:
609 mStatusIcon = R.drawable.common_error;
610 mStatusText = R.string.auth_not_configured_title;
611 break;
612 case UNKNOWN_ERROR:
613 mStatusIcon = R.drawable.common_error;
614 mStatusText = R.string.auth_unknown_error_title;
615 break;
616 case FILE_NOT_FOUND:
617 mStatusIcon = R.drawable.common_error;
618 mStatusText = R.string.auth_incorrect_path_title;
619 break;
620 default:
621 Log.e(TAG, "Incorrect connection checker result type: " + type);
622 }
623 setResultIconAndText(mStatusIcon, mStatusText);
624 if (!mStatusCorrect)
625 findViewById(R.id.refreshButton).setVisibility(View.VISIBLE);
626 else
627 findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);
628 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
629 }
630
631 public void onFocusChange(View view, boolean hasFocus) {
632 if (view.getId() == R.id.host_URL) {
633 if (!hasFocus) {
634 TextView tv = ((TextView) findViewById(R.id.host_URL));
635 String uri = tv.getText().toString().trim();
636 if (uri.length() != 0) {
637 setResultIconAndText(R.drawable.progress_small,
638 R.string.auth_testing_connection);
639 //mConnChkRunnable = new ConnectionCheckerRunnable(uri, this);
640 mConnChkRunnable = new ConnectionCheckOperation(uri, this);
641 //mConnChkRunnable.setListener(this, mHandler);
642 //mAuthThread = new Thread(mConnChkRunnable);
643 //mAuthThread.start();
644 WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this);
645 mDiscoveredVersion = null;
646 mAuthThread = mConnChkRunnable.execute(client, this, mHandler);
647 } else {
648 findViewById(R.id.refreshButton).setVisibility(
649 View.INVISIBLE);
650 setResultIconAndText(0, 0);
651 }
652 } else {
653 // avoids that the 'connect' button can be clicked if the test was previously passed
654 findViewById(R.id.buttonOK).setEnabled(false);
655 }
656 } else if (view.getId() == R.id.account_password) {
657 ImageView iv = (ImageView) findViewById(R.id.viewPassword);
658 if (hasFocus) {
659 iv.setVisibility(View.VISIBLE);
660 } else {
661 TextView v = (TextView) findViewById(R.id.account_password);
662 int input_type = InputType.TYPE_CLASS_TEXT
663 | InputType.TYPE_TEXT_VARIATION_PASSWORD;
664 v.setInputType(input_type);
665 iv.setVisibility(View.INVISIBLE);
666 }
667 }
668 }
669
670 private void setResultIconAndText(int drawable_id, int text_id) {
671 ImageView iv = (ImageView) findViewById(R.id.action_indicator);
672 TextView tv = (TextView) findViewById(R.id.status_text);
673
674 if (drawable_id == 0 && text_id == 0) {
675 iv.setVisibility(View.INVISIBLE);
676 tv.setVisibility(View.INVISIBLE);
677 } else {
678 iv.setImageResource(drawable_id);
679 tv.setText(text_id);
680 iv.setVisibility(View.VISIBLE);
681 tv.setVisibility(View.VISIBLE);
682 }
683 }
684
685 @Override
686 public void onClick(View v) {
687 if (v.getId() == R.id.refreshButton) {
688 onFocusChange(findViewById(R.id.host_URL), false);
689 } else if (v.getId() == R.id.viewPassword) {
690 TextView view = (TextView) findViewById(R.id.account_password);
691 int input_type = view.getInputType();
692 if ((input_type & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
693 input_type = InputType.TYPE_CLASS_TEXT
694 | InputType.TYPE_TEXT_VARIATION_PASSWORD;
695 } else {
696 input_type = InputType.TYPE_CLASS_TEXT
697 | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
698 }
699 view.setInputType(input_type);
700 }
701 }
702
703 @Override protected void onDestroy() {
704 // We must stop the service thats it's pooling to oAuth2 server for a token.
705 Intent tokenService = new Intent(this, OAuth2GetTokenService.class);
706 stopService(tokenService);
707
708 // We stop listening the result of the pooling service.
709 if (tokenReceiver != null) {
710 unregisterReceiver(tokenReceiver);
711 tokenReceiver = null;
712 finish();
713 }
714
715 super.onDestroy();
716 }
717
718 // Controlling the oAuth2 checkbox on the activity: hide and show widgets.
719 public void onOff_check_Click(View view) {
720 CheckBox oAuth2Check = (CheckBox)view;
721 changeViewByOAuth2Check(oAuth2Check.isChecked());
722
723 }
724
725 public void changeViewByOAuth2Check(Boolean checked) {
726
727 EditText oAuth2Url = (EditText) findViewById(R.id.oAuth_URL);
728 EditText accountUsername = (EditText) findViewById(R.id.account_username);
729 EditText accountPassword = (EditText) findViewById(R.id.account_password);
730 ImageView viewPassword = (ImageView) findViewById(R.id.viewPassword);
731 ImageView auth2ActionIndicator = (ImageView) findViewById(R.id.auth2_action_indicator);
732 TextView oauth2StatusText = (TextView) findViewById(R.id.oauth2_status_text);
733
734 if (checked) {
735 oAuth2Url.setVisibility(View.VISIBLE);
736 accountUsername.setVisibility(View.GONE);
737 accountPassword.setVisibility(View.GONE);
738 viewPassword.setVisibility(View.GONE);
739 auth2ActionIndicator.setVisibility(View.INVISIBLE);
740 oauth2StatusText.setVisibility(View.INVISIBLE);
741 } else {
742 oAuth2Url.setVisibility(View.GONE);
743 accountUsername.setVisibility(View.VISIBLE);
744 accountPassword.setVisibility(View.VISIBLE);
745 viewPassword.setVisibility(View.INVISIBLE);
746 auth2ActionIndicator.setVisibility(View.GONE);
747 oauth2StatusText.setVisibility(View.GONE);
748 }
749
750 }
751
752 // Controlling the oAuth2 result of server connection.
753 private void setOAuth2ResultIconAndText(int drawable_id, int text_id) {
754 ImageView iv = (ImageView) findViewById(R.id.auth2_action_indicator);
755 TextView tv = (TextView) findViewById(R.id.oauth2_status_text);
756
757 if (drawable_id == 0 && text_id == 0) {
758 iv.setVisibility(View.INVISIBLE);
759 tv.setVisibility(View.INVISIBLE);
760 } else {
761 iv.setImageResource(drawable_id);
762 tv.setText(text_id);
763 iv.setVisibility(View.VISIBLE);
764 tv.setVisibility(View.VISIBLE);
765 }
766 }
767
768 // Results from the first call to oAuth2 server : getting the user_code and verification_url.
769 @Override
770 public void onOAuth2GetCodeResult(ResultOAuthType type, JSONObject responseJson) {
771 if ((type == ResultOAuthType.OK_SSL)||(type == ResultOAuthType.OK_NO_SSL)) {
772 codeResponseJson = responseJson;
773 if (codeResponseJson != null) {
774 getOAuth2AccessTokenFromJsonResponse();
775 } // else - nothing to do here - wait for callback !!!
776
777 } else if (type == ResultOAuthType.HOST_NOT_AVAILABLE) {
778 setOAuth2ResultIconAndText(R.drawable.common_error, R.string.oauth_connection_url_unavailable);
779 }
780 }
781
782 // If the results of getting the user_code and verification_url are OK, we get the received data and we start
783 // the polling service to oAuth2 server to get a valid token.
784 private void getOAuth2AccessTokenFromJsonResponse() {
785 String deviceCode = null;
786 String verificationUrl = null;
787 String userCode = null;
788 int expiresIn = -1;
789 int interval = -1;
790
791 Log.d(TAG, "ResponseOAuth2->" + codeResponseJson.toString());
792
793 try {
794 // We get data that we must show to the user or we will use internally.
795 verificationUrl = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL);
796 userCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE);
797 expiresIn = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_EXPIRES_IN);
798
799 // And we get data that we must use to get a token.
800 deviceCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_DEVICE_CODE);
801 interval = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_INTERVAL);
802
803 } catch (JSONException e) {
804 Log.e(TAG, "Exception accesing data in Json object" + e.toString());
805 }
806
807 // Updating status widget to OK.
808 setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established);
809
810 // Showing the dialog with instructions for the user.
811 showDialog(OAUTH2_LOGIN_PROGRESS);
812
813 // Loggin all the data.
814 Log.d(TAG, "verificationUrl->" + verificationUrl);
815 Log.d(TAG, "userCode->" + userCode);
816 Log.d(TAG, "deviceCode->" + deviceCode);
817 Log.d(TAG, "expiresIn->" + expiresIn);
818 Log.d(TAG, "interval->" + interval);
819
820 // Starting the pooling service.
821 try {
822 Intent tokenService = new Intent(this, OAuth2GetTokenService.class);
823 tokenService.putExtra(OAuth2GetTokenService.TOKEN_URI, OAuth2Context.OAUTH2_G_DEVICE_GETTOKEN_URL);
824 tokenService.putExtra(OAuth2GetTokenService.TOKEN_DEVICE_CODE, deviceCode);
825 tokenService.putExtra(OAuth2GetTokenService.TOKEN_INTERVAL, interval);
826
827 startService(tokenService);
828 }
829 catch (Exception e) {
830 Log.e(TAG, "tokenService creation problem :", e);
831 }
832
833 }
834
835 private void getOAuth2AccessTokenFromCapturedRedirection() {
836 Map<String, String> responseValues = new HashMap<String, String>();
837 //String queryParameters = getIntent().getData().getQuery();
838 String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();
839 mNewCapturedUriFromOAuth2Redirection = null;
840
841 Log.v(TAG, "Queryparameters (Code) = " + queryParameters);
842
843 String[] pairs = queryParameters.split("&");
844 Log.v(TAG, "Pairs (Code) = " + pairs.toString());
845
846 int i = 0;
847 String key = "";
848 String value = "";
849
850 StringBuilder sb = new StringBuilder();
851
852 while (pairs.length > i) {
853 int j = 0;
854 String[] part = pairs[i].split("=");
855
856 while (part.length > j) {
857 String p = part[j];
858 if (j == 0) {
859 key = p;
860 sb.append(key + " = ");
861 } else if (j == 1) {
862 value = p;
863 responseValues.put(key, value);
864 sb.append(value + "\n");
865 }
866
867 Log.v(TAG, "[" + i + "," + j + "] = " + p);
868 j++;
869 }
870 i++;
871 }
872
873
874 // Updating status widget to OK.
875 setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established);
876
877 // Showing the dialog with instructions for the user.
878 showDialog(OAUTH2_LOGIN_PROGRESS);
879
880 //
881 RemoteOperation operation = new GetOAuth2AccessToken(responseValues);
882 WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(OAuth2Context.OAUTH2_F_TOKEN_ENDPOINT_URL), getApplicationContext());
883 operation.execute(client, this, mHandler);
884 }
885
886
887
888 // We get data from the oAuth2 token service with this broadcast receiver.
889 private class TokenReceiver extends BroadcastReceiver {
890 /**
891 * The token is received.
892 * @author
893 * {@link BroadcastReceiver} to enable oAuth2 token receiving.
894 */
895 @Override
896 public void onReceive(Context context, Intent intent) {
897 @SuppressWarnings("unchecked")
898 HashMap<String, String> tokenResponse = (HashMap<String, String>)intent.getExtras().get(OAuth2GetTokenService.TOKEN_RECEIVED_DATA);
899 Log.d(TAG, "TokenReceiver->" + tokenResponse.get(OAuth2GetTokenService.TOKEN_ACCESS_TOKEN));
900 dismissDialog(OAUTH2_LOGIN_PROGRESS);
901
902 }
903 }
904
905 @Override
906 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
907 if (operation instanceof ConnectionCheckOperation) {
908
909 mStatusText = mStatusIcon = 0;
910 mStatusCorrect = false;
911 String t_url = ((TextView) findViewById(R.id.host_URL)).getText()
912 .toString().trim().toLowerCase();
913
914 switch (result.getCode()) {
915 case OK_SSL:
916 mIsSslConn = true;
917 mStatusIcon = android.R.drawable.ic_secure;
918 mStatusText = R.string.auth_secure_connection;
919 mStatusCorrect = true;
920 break;
921
922 case OK_NO_SSL:
923 case OK:
924 mIsSslConn = false;
925 mStatusCorrect = true;
926 if (t_url.startsWith("http://") ) {
927 mStatusText = R.string.auth_connection_established;
928 mStatusIcon = R.drawable.ic_ok;
929 } else {
930 mStatusText = R.string.auth_nossl_plain_ok_title;
931 mStatusIcon = android.R.drawable.ic_partial_secure;
932 }
933 break;
934
935
936 case BAD_OC_VERSION:
937 mStatusIcon = R.drawable.common_error;
938 mStatusText = R.string.auth_bad_oc_version_title;
939 break;
940 case WRONG_CONNECTION:
941 mStatusIcon = R.drawable.common_error;
942 mStatusText = R.string.auth_wrong_connection_title;
943 break;
944 case TIMEOUT:
945 mStatusIcon = R.drawable.common_error;
946 mStatusText = R.string.auth_timeout_title;
947 break;
948 case INCORRECT_ADDRESS:
949 mStatusIcon = R.drawable.common_error;
950 mStatusText = R.string.auth_incorrect_address_title;
951 break;
952
953 case SSL_RECOVERABLE_PEER_UNVERIFIED:
954 mStatusIcon = R.drawable.common_error;
955 mStatusText = R.string.auth_ssl_unverified_server_title;
956 mLastSslUntrustedServerResult = result;
957 showDialog(DIALOG_SSL_VALIDATOR);
958 break;
959
960 case SSL_ERROR:
961 mStatusIcon = R.drawable.common_error;
962 mStatusText = R.string.auth_ssl_general_error_title;
963 break;
964
965 case HOST_NOT_AVAILABLE:
966 mStatusIcon = R.drawable.common_error;
967 mStatusText = R.string.auth_unknown_host_title;
968 break;
969 case NO_NETWORK_CONNECTION:
970 mStatusIcon = R.drawable.no_network;
971 mStatusText = R.string.auth_no_net_conn_title;
972 break;
973 case INSTANCE_NOT_CONFIGURED:
974 mStatusIcon = R.drawable.common_error;
975 mStatusText = R.string.auth_not_configured_title;
976 break;
977 case FILE_NOT_FOUND:
978 mStatusIcon = R.drawable.common_error;
979 mStatusText = R.string.auth_incorrect_path_title;
980 break;
981 case UNHANDLED_HTTP_CODE:
982 case UNKNOWN_ERROR:
983 mStatusIcon = R.drawable.common_error;
984 mStatusText = R.string.auth_unknown_error_title;
985 break;
986 default:
987 Log.e(TAG, "Incorrect connection checker result type: " + result.getHttpCode());
988 }
989 setResultIconAndText(mStatusIcon, mStatusText);
990 if (!mStatusCorrect)
991 findViewById(R.id.refreshButton).setVisibility(View.VISIBLE);
992 else
993 findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);
994 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
995
996 } else if (operation instanceof GetOAuth2AccessToken) {
997
998 try {
999 dismissDialog(OAUTH2_LOGIN_PROGRESS);
1000 } catch (IllegalArgumentException e) {
1001 // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens
1002 }
1003
1004 if (result.isSuccess()) {
1005
1006 /// time to test the retrieved access token on the ownCloud server
1007 String url = ((TextView) findViewById(R.id.host_URL)).getText()
1008 .toString().trim();
1009 if (url.endsWith("/"))
1010 url = url.substring(0, url.length() - 1);
1011
1012 Uri uri = null;
1013 /*String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion);
1014
1015 if (webdav_path == null) {
1016 onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title));
1017 return;
1018 }*/
1019
1020 String prefix = "";
1021 if (mIsSslConn) {
1022 prefix = "https://";
1023 } else {
1024 prefix = "http://";
1025 }
1026 if (url.toLowerCase().startsWith("http://")
1027 || url.toLowerCase().startsWith("https://")) {
1028 prefix = "";
1029 }
1030
1031 try {
1032 mBaseUrl = prefix + url;
1033 //String url_str = prefix + url + webdav_path;
1034 String url_str = prefix + url + "/remote.php/odav";
1035 uri = Uri.parse(url_str);
1036
1037 } catch (Exception e) {
1038 // should never happen
1039 onAuthenticationResult(false, getString(R.string.auth_incorrect_address_title));
1040 return;
1041 }
1042
1043 showDialog(DIALOG_LOGIN_PROGRESS);
1044 String accessToken = ((GetOAuth2AccessToken)operation).getResultTokenMap().get(OAuth2Context.KEY_ACCESS_TOKEN);
1045 Log.d(TAG, "Got ACCESS TOKEN: " + accessToken);
1046 mAuthChkOperation = new ExistenceCheckOperation("", this, accessToken);
1047 WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(uri, getApplicationContext());
1048 mAuthChkOperation.execute(client, this, mHandler);
1049
1050
1051 } else {
1052 TextView tv = (TextView) findViewById(R.id.oAuth_URL);
1053 tv.setError("A valid authorization could not be obtained");
1054
1055 }
1056
1057 } else if (operation instanceof ExistenceCheckOperation) {
1058
1059 try {
1060 dismissDialog(DIALOG_LOGIN_PROGRESS);
1061 } catch (IllegalArgumentException e) {
1062 // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens
1063 }
1064
1065 if (result.isSuccess()) {
1066 TextView tv = (TextView) findViewById(R.id.oAuth_URL);
1067 Log.d(TAG, "Checked access - time to save the account");
1068
1069 Uri uri = Uri.parse(mBaseUrl);
1070 String username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();
1071 String accountName = username + "@" + uri.getHost();
1072 if (uri.getPort() >= 0) {
1073 accountName += ":" + uri.getPort();
1074 }
1075 // TODO - check that accountName does not exist
1076 Account account = new Account(accountName, AccountAuthenticator.ACCOUNT_TYPE);
1077 AccountManager accManager = AccountManager.get(this);
1078 accManager.addAccountExplicitly(account, "", null); // with our implementation, the password is never input in the app
1079
1080 // Add this account as default in the preferences, if there is none
1081 Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);
1082 if (defaultAccount == null) {
1083 SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
1084 editor.putString("select_oc_account", accountName);
1085 editor.commit();
1086 }
1087
1088 /// account data to save by the AccountManager
1089 final Intent intent = new Intent();
1090 intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountAuthenticator.ACCOUNT_TYPE);
1091 intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
1092 intent.putExtra(AccountManager.KEY_USERDATA, username);
1093
1094 accManager.setAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, ((ExistenceCheckOperation) operation).getAccessToken());
1095
1096 accManager.setUserData(account, AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable.getDiscoveredVersion().toString());
1097 accManager.setUserData(account, AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);
1098 accManager.setUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE");
1099
1100 setAccountAuthenticatorResult(intent.getExtras());
1101 setResult(RESULT_OK, intent);
1102
1103 /// enforce the first account synchronization
1104 Bundle bundle = new Bundle();
1105 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
1106 ContentResolver.requestSync(account, "org.owncloud", bundle);
1107
1108 finish();
1109
1110 } else {
1111 TextView tv = (TextView) findViewById(R.id.oAuth_URL);
1112 tv.setError(result.getLogMessage());
1113 Log.d(TAG, "Access failed: " + result.getLogMessage());
1114 }
1115 }
1116 }
1117
1118
1119 public void onSavedCertificate() {
1120 mAuthThread = mConnChkRunnable.retry(this, mHandler);
1121 }
1122
1123 @Override
1124 public void onFailedSavingCertificate() {
1125 showDialog(DIALOG_CERT_NOT_SAVED);
1126 }
1127
1128 }