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