/**
* Copyright (c) 2013 LEAP Encryption Access Project and contributers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package se.leap.bitmaskclient;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.json.JSONException;
import org.json.JSONObject;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.ProviderAPIResultReceiver.Receiver;
import se.leap.openvpn.MainActivity;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.CompoundButton;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
/**
* The main user facing Activity of LEAP Android, consisting of status, controls,
* and access to preferences.
*
* @author Sean Leonard
* @author parmegv
*/
public class Dashboard extends Activity implements LogInDialog.LogInDialogInterface,Receiver {
protected static final int CONFIGURE_LEAP = 0;
protected static final int SWITCH_PROVIDER = 1;
private static final String TAG_EIP_FRAGMENT = "EIP_DASHBOARD_FRAGMENT";
final public static String SHARED_PREFERENCES = "LEAPPreferences";
final public static String ACTION_QUIT = "quit";
public static final String REQUEST_CODE = "request_code";
private ProgressBar mProgressBar;
private TextView eipStatus;
private ProviderAPIBroadcastReceiver_Update providerAPI_broadcast_receiver_update;
private static Context app;
private static SharedPreferences preferences;
private static Provider provider;
private TextView providerNameTV;
private boolean authed_eip = false;
public ProviderAPIResultReceiver providerAPI_result_receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = this;
PRNGFixes.apply();
// mProgressBar = (ProgressBar) findViewById(R.id.progressbar_dashboard);
// mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
// eipStatus = (TextView) findViewById(R.id.eipStatus);
providerAPI_broadcast_receiver_update = new ProviderAPIBroadcastReceiver_Update();
IntentFilter update_intent_filter = new IntentFilter(ProviderAPI.UPDATE_ACTION);
update_intent_filter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(providerAPI_broadcast_receiver_update, update_intent_filter);
ConfigHelper.setSharedPreferences(getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE));
preferences = ConfigHelper.shared_preferences;
authed_eip = ConfigHelper.getBoolFromSharedPref(EIP.AUTHED_EIP);
if (ConfigHelper.getStringFromSharedPref(Provider.KEY).isEmpty())
startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
else
buildDashboard();
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(providerAPI_broadcast_receiver_update);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if ( requestCode == CONFIGURE_LEAP || requestCode == SWITCH_PROVIDER) {
// It should be equivalent: if ( (requestCode == CONFIGURE_LEAP) || (data!= null && data.hasExtra(STOP_FIRST))) {
if ( resultCode == RESULT_OK ){
ConfigHelper.saveSharedPref(EIP.PARSED_SERIAL, 0);
ConfigHelper.saveSharedPref(EIP.AUTHED_EIP, authed_eip);
startService( new Intent(EIP.ACTION_UPDATE_EIP_SERVICE) );
buildDashboard();
if(data != null && data.hasExtra(LogInDialog.VERB)) {
View view = ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
logInDialog(view, Bundle.EMPTY);
}
} else if(resultCode == RESULT_CANCELED && data.hasExtra(ACTION_QUIT)) {
finish();
} else
configErrorDialog();
}
}
/**
* Dialog shown when encountering a configuration error. Such errors require
* reconfiguring LEAP or aborting the application.
*/
private void configErrorDialog() {
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getAppContext());
alertBuilder.setTitle(getResources().getString(R.string.setup_error_title));
alertBuilder
.setMessage(getResources().getString(R.string.setup_error_text))
.setCancelable(false)
.setPositiveButton(getResources().getString(R.string.setup_error_configure_button), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startActivityForResult(new Intent(getAppContext(),ConfigurationWizard.class),CONFIGURE_LEAP);
}
})
.setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences.Editor prefsEdit = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE).edit();
prefsEdit.remove(Provider.KEY).commit();
finish();
}
})
.show();
}
/**
* Inflates permanent UI elements of the View and contains logic for what
* service dependent UI elements to include.
*/
private void buildDashboard() {
provider = Provider.getInstance();
provider.init( this );
setContentView(R.layout.client_dashboard);
providerAPI_broadcast_receiver_update = new ProviderAPIBroadcastReceiver_Update();
IntentFilter update_intent_filter = new IntentFilter(ProviderAPI.UPDATE_ACTION);
update_intent_filter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(providerAPI_broadcast_receiver_update, update_intent_filter);
providerNameTV = (TextView) findViewById(R.id.providerName);
providerNameTV.setText(provider.getDomain());
providerNameTV.setTextSize(28);
FragmentManager fragMan = getFragmentManager();
if ( provider.hasEIP()){
EipServiceFragment eipFragment = new EipServiceFragment();
fragMan.beginTransaction().replace(R.id.servicesCollection, eipFragment, TAG_EIP_FRAGMENT).commit();
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
JSONObject provider_json;
try {
provider_json = ConfigHelper.getJsonFromSharedPref(Provider.KEY);
JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE);
if(service_description.getBoolean(Provider.ALLOW_REGISTRATION)) {
if(authed_eip) {
menu.findItem(R.id.login_button).setVisible(false);
menu.findItem(R.id.logout_button).setVisible(true);
} else {
menu.findItem(R.id.login_button).setVisible(true);
menu.findItem(R.id.logout_button).setVisible(false);
}
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.client_dashboard, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
Intent intent;
switch (item.getItemId()){
case R.id.about_leap:
Fragment aboutFragment = new AboutFragment();
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.dashboardLayout, aboutFragment);
trans.addToBackStack(null);
trans.commit();
return true;
case R.id.legacy_interface:
intent = new Intent(this,MainActivity.class);
startActivity(intent);
return true;
case R.id.switch_provider:
if (Provider.getInstance().hasEIP()){
if (ConfigHelper.getBoolFromSharedPref(EIP.AUTHED_EIP)){
logOut();
}
eipStop();
}
startActivityForResult(new Intent(this,ConfigurationWizard.class), SWITCH_PROVIDER);
return true;
case R.id.login_button:
View view = ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
logInDialog(view, Bundle.EMPTY);
return true;
case R.id.logout_button:
logOut();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void authenticate(String username, String password) {
mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
eipStatus = (TextView) findViewById(R.id.eipStatus);
providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
providerAPI_result_receiver.setReceiver(this);
Intent provider_API_command = new Intent(this, ProviderAPI.class);
Bundle parameters = new Bundle();
parameters.putString(LogInDialog.USERNAME, username);
parameters.putString(LogInDialog.PASSWORD, password);
JSONObject provider_json;
try {
provider_json = new JSONObject(preferences.getString(Provider.KEY, ""));
parameters.putString(Provider.API_URL, provider_json.getString(Provider.API_URL) + "/" + provider_json.getString(Provider.API_VERSION));
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
provider_API_command.setAction(ProviderAPI.SRP_AUTH);
provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
mProgressBar.setVisibility(ProgressBar.VISIBLE);
eipStatus.setText("Starting to login");
//mProgressBar.setMax(4);
startService(provider_API_command);
}
/**
* Asks ProviderAPI to log out.
*/
public void logOut() {
providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
providerAPI_result_receiver.setReceiver(this);
Intent provider_API_command = new Intent(this, ProviderAPI.class);
Bundle parameters = new Bundle();
JSONObject provider_json;
try {
provider_json = new JSONObject(preferences.getString(Provider.KEY, ""));
parameters.putString(Provider.API_URL, provider_json.getString(Provider.API_URL) + "/" + provider_json.getString(Provider.API_VERSION));
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
provider_API_command.setAction(ProviderAPI.LOG_OUT);
provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
if(mProgressBar == null) mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
mProgressBar.setVisibility(ProgressBar.VISIBLE);
if(eipStatus == null) eipStatus = (TextView) findViewById(R.id.eipStatus);
eipStatus.setText("Starting to logout");
startService(provider_API_command);
//mProgressBar.setMax(1);
}
/**
* Shows the log in dialog.
* @param view from which the dialog is created.
*/
public void logInDialog(View view, Bundle resultData) {
FragmentTransaction fragment_transaction = getFragmentManager().beginTransaction();
Fragment previous_log_in_dialog = getFragmentManager().findFragmentByTag(LogInDialog.TAG);
if (previous_log_in_dialog != null) {
fragment_transaction.remove(previous_log_in_dialog);
}
fragment_transaction.addToBackStack(null);
DialogFragment newFragment = LogInDialog.newInstance();
if(resultData != null && !resultData.isEmpty()) {
newFragment.setArguments(resultData);
}
newFragment.show(fragment_transaction, LogInDialog.TAG);
}
/**
* Asks ProviderAPI to download an authenticated OpenVPN certificate.
* @param session_id cookie for the server to allow us to download the certificate.
*/
private void downloadAuthedUserCertificate(/*Cookie session_id*/) {
providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
providerAPI_result_receiver.setReceiver(this);
Intent provider_API_command = new Intent(this, ProviderAPI.class);
Bundle parameters = new Bundle();
parameters.putString(ConfigurationWizard.TYPE_OF_CERTIFICATE, ConfigurationWizard.AUTHED_CERTIFICATE);
/*parameters.putString(ConfigHelper.SESSION_ID_COOKIE_KEY, session_id.getName());
parameters.putString(ConfigHelper.SESSION_ID_KEY, session_id.getValue());*/
provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
startService(provider_API_command);
}
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
if(resultCode == ProviderAPI.SRP_AUTHENTICATION_SUCCESSFUL){
String session_id_cookie_key = resultData.getString(ProviderAPI.SESSION_ID_COOKIE_KEY);
String session_id_string = resultData.getString(ProviderAPI.SESSION_ID_KEY);
setResult(RESULT_OK);
authed_eip = true;
ConfigHelper.saveSharedPref(EIP.AUTHED_EIP, authed_eip);
invalidateOptionsMenu();
mProgressBar.setVisibility(ProgressBar.GONE);
//Cookie session_id = new BasicClientCookie(session_id_cookie_key, session_id_string);
downloadAuthedUserCertificate(/*session_id*/);
} else if(resultCode == ProviderAPI.SRP_AUTHENTICATION_FAILED) {
logInDialog(getCurrentFocus(), resultData);
eipStatus.setText("Login failed");
mProgressBar.setVisibility(ProgressBar.GONE);
} else if(resultCode == ProviderAPI.LOGOUT_SUCCESSFUL) {
authed_eip = false;
ConfigHelper.saveSharedPref(EIP.AUTHED_EIP, authed_eip);
changeStatusMessage(resultCode);
mProgressBar.setVisibility(ProgressBar.GONE);
mProgressBar.setProgress(0);
invalidateOptionsMenu();
setResult(RESULT_OK);
} else if(resultCode == ProviderAPI.LOGOUT_FAILED) {
setResult(RESULT_CANCELED);
eipStatus.setText("Didn't log out");
mProgressBar.setVisibility(ProgressBar.GONE);
//mProgressBar.setProgress(0);
Toast.makeText(getApplicationContext(), R.string.log_out_failed_message, Toast.LENGTH_LONG).show();
} else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) {
setResult(RESULT_OK);
mProgressBar.setVisibility(ProgressBar.GONE);
changeStatusMessage(resultCode);
//mProgressBar.setProgress(0);
Toast.makeText(getApplicationContext(), R.string.successful_authed_cert_downloaded_message, Toast.LENGTH_LONG).show();
} else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
setResult(RESULT_CANCELED);
mProgressBar.setVisibility(ProgressBar.GONE);
//mProgressBar.setProgress(0);
changeStatusMessage(resultCode);
Toast.makeText(getApplicationContext(), R.string.authed_cert_download_failed_message, Toast.LENGTH_LONG).show();
}
}
private void changeStatusMessage(final int previous_result_code) {
// TODO Auto-generated method stub
ResultReceiver eip_status_receiver = new ResultReceiver(new Handler()){
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
String request = resultData.getString(EIP.REQUEST_TAG);
if(resultCode == RESULT_OK) {
if(request.equalsIgnoreCase(EIP.ACTION_IS_EIP_RUNNING)) {
switch (previous_result_code) {
case ProviderAPI.LOGOUT_SUCCESSFUL: eipStatus.setText(R.string.anonymous_secured_status); break;
case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: eipStatus.setText(R.string.authed_secured_status); break;
}
}
} else {
if(request.equalsIgnoreCase(EIP.ACTION_IS_EIP_RUNNING)) {
switch (previous_result_code) {
case ProviderAPI.LOGOUT_SUCCESSFUL: eipStatus.setText(R.string.future_anonymous_secured_status); break;
case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: eipStatus.setText(R.string.future_authed_secured_status); break;
}
}
}
}
};
eipIsRunning(eip_status_receiver);
}
/**
* For retrieving the base application Context in classes that don't extend
* Android's Activity class
*
* @return Application Context as defined by this
for Dashboard instance
*/
public static Context getAppContext() {
return app;
}
@Override
public void startActivityForResult(Intent intent, int requestCode) {
intent.putExtra(Dashboard.REQUEST_CODE, requestCode);
super.startActivityForResult(intent, requestCode);
}
/**
* Send a command to EIP
*
* @param action A valid String constant from EIP class representing an Intent
* filter for the EIP class
*/
private void eipIsRunning(ResultReceiver eip_receiver){
// TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
Intent eip_intent = new Intent(this, EIP.class);
eip_intent.setAction(EIP.ACTION_IS_EIP_RUNNING);
eip_intent.putExtra(EIP.RECEIVER_TAG, eip_receiver);
startService(eip_intent);
}
/**
* Send a command to EIP
*
*/
private void eipStop(){
// TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
Intent eip_intent = new Intent(this, EIP.class);
eip_intent.setAction(EIP.ACTION_STOP_EIP);
// eip_intent.putExtra(EIP.RECEIVER_TAG, eip_receiver);
startService(eip_intent);
}
public class ProviderAPIBroadcastReceiver_Update extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int update = intent.getIntExtra(ProviderAPI.UPDATE_DATA, 0);
//mProgressBar.setProgress(update);
}
}
}