summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/AboutActivity.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java13
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Dashboard.java682
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EIP.java631
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java505
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java55
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Provider.java226
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderListAdapter.java104
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderListFragment.java234
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderManager.java178
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderRenderer.java57
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderRendererBuilder.java25
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/SessionDialog.java (renamed from app/src/main/java/se/leap/bitmaskclient/LogInDialog.java)101
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/SignUpDialog.java147
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java42
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Constants.java47
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java251
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java138
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java156
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java46
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java (renamed from app/src/main/java/se/leap/bitmaskclient/VoidVpnLauncher.java)6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java33
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java60
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java145
28 files changed, 1969 insertions, 1930 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java b/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java
index 6d025422..6c4e517b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java
@@ -1,15 +1,10 @@
package se.leap.bitmaskclient;
import android.app.Activity;
-import android.app.Fragment;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
import android.widget.TextView;
-import se.leap.bitmaskclient.R;
public class AboutActivity extends Activity {
diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
index c95d0c8b..c0f0b0c3 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
@@ -16,11 +16,15 @@
*/
package se.leap.bitmaskclient;
+import android.util.Base64;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
-import java.lang.IllegalArgumentException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
@@ -33,13 +37,6 @@ import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Base64;
-
/**
* Stores constants, and implements auxiliary methods used across all LEAP Android classes.
*
diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index 761afc0a..862086eb 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -14,20 +14,12 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
- package se.leap.bitmaskclient;
+package se.leap.bitmaskclient;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.ProviderAPIResultReceiver.Receiver;
-import se.leap.bitmaskclient.SignUpDialog;
-import de.blinkt.openvpn.activities.LogWindow;
+import android.annotation.SuppressLint;
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.content.Context;
import android.content.DialogInterface;
@@ -40,24 +32,36 @@ 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.widget.ProgressBar;
import android.widget.TextView;
-import android.widget.Toast;
+
+import org.jetbrains.annotations.NotNull;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+import de.blinkt.openvpn.activities.LogWindow;
+import se.leap.bitmaskclient.eip.Constants;
+import se.leap.bitmaskclient.eip.EIP;
+import se.leap.bitmaskclient.eip.EipStatus;
/**
- * The main user facing Activity of LEAP Android, consisting of status, controls,
+ * The main user facing Activity of Bitmask Android, consisting of status, controls,
* and access to preferences.
*
* @author Sean Leonard <meanderingcode@aetherislands.net>
* @author parmegv
*/
-public class Dashboard extends Activity implements LogInDialog.LogInDialogInterface, SignUpDialog.SignUpDialogInterface, Receiver {
+public class Dashboard extends Activity implements SessionDialog.SessionDialogInterface, ProviderAPIResultReceiver.Receiver {
protected static final int CONFIGURE_LEAP = 0;
protected static final int SWITCH_PROVIDER = 1;
+ final public static String TAG = Dashboard.class.getSimpleName();
final public static String SHARED_PREFERENCES = "LEAPPreferences";
final public static String ACTION_QUIT = "quit";
public static final String REQUEST_CODE = "request_code";
@@ -66,92 +70,118 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
final public static String ON_BOOT = "dashboard on boot";
public static final String APP_VERSION = "bitmask version";
+ private static Context app;
+ protected static SharedPreferences preferences;
+ private FragmentManagerEnhanced fragment_manager;
- private EipServiceFragment eipFragment;
- private ProgressBar mProgressBar;
- private TextView eipStatus;
- private static Context app;
- protected static SharedPreferences preferences;
- private static Provider provider;
-
- private TextView providerNameTV;
-
- private boolean authed_eip = false;
+ @InjectView(R.id.providerName)
+ TextView provider_name;
+ EipServiceFragment eip_fragment;
+ private Provider provider;
+ private static boolean authed_eip;
public ProviderAPIResultReceiver providerAPI_result_receiver;
- @Override
+ @Override
+ protected void onSaveInstanceState(@NotNull Bundle outState) {
+ if(provider != null)
+ outState.putParcelable(Provider.KEY, provider);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = this;
PRNGFixes.apply();
-
- mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+ fragment_manager = new FragmentManagerEnhanced(getFragmentManager());
handleVersion();
-
- authed_eip = preferences.getBoolean(EIP.AUTHED_EIP, false);
- if (preferences.getString(Provider.KEY, "").isEmpty())
- startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
- else
- buildDashboard(getIntent().getBooleanExtra(ON_BOOT, false));
+
+ provider = getSavedProvider(savedInstanceState);
+ if (provider == null || provider.getName().isEmpty())
+ startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
+ else
+ buildDashboard(getIntent().getBooleanExtra(ON_BOOT, false));
}
+ private Provider getSavedProvider(Bundle savedInstanceState) {
+ Provider provider = null;
+ if(savedInstanceState != null)
+ provider = savedInstanceState.getParcelable(Provider.KEY);
+ else if(preferences.getBoolean(Constants.PROVIDER_CONFIGURED, false))
+ provider = getSavedProviderFromSharedPreferences();
+
+ return provider;
+ }
+
+ private Provider getSavedProviderFromSharedPreferences() {
+ Provider provider = null;
+ try {
+ provider = new Provider(new URL(preferences.getString(Provider.MAIN_URL, "")));
+ provider.define(new JSONObject(preferences.getString(Provider.KEY, "")));
+ } catch (MalformedURLException | JSONException e) {
+ e.printStackTrace();
+ }
+
+ return provider;
+ }
+
+
private void handleVersion() {
try {
int versionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
int lastDetectedVersion = preferences.getInt(APP_VERSION, 0);
- preferences.edit().putInt(APP_VERSION, versionCode);
+ preferences.edit().putInt(APP_VERSION, versionCode).apply();
Log.d("Dashboard", "detected version code: " + versionCode);
Log.d("Dashboard", "last detected version code: " + lastDetectedVersion);
switch(versionCode) {
case 91: // 0.6.0 without Bug #5999
- if(!preferences.getString(EIP.KEY, "").isEmpty()) {
+ case 101: // 0.8.0
+ if(!preferences.getString(Constants.KEY, "").isEmpty()) {
Intent rebuildVpnProfiles = new Intent(getApplicationContext(), EIP.class);
- rebuildVpnProfiles.setAction(EIP.ACTION_REBUILD_PROFILES);
+ rebuildVpnProfiles.setAction(Constants.ACTION_UPDATE_EIP_SERVICE);
startService(rebuildVpnProfiles);
}
+ break;
}
} catch (NameNotFoundException e) {
+ Log.d(TAG, "Handle version didn't find any " + getPackageName() + " package");
}
}
-
- @Override
- protected void onDestroy() {
-
- super.onDestroy();
- }
-
- protected void onPause() {
- super.onPause();
- }
- @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 ){
- preferences.edit().putInt(EIP.PARSED_SERIAL, 0).commit();
- preferences.edit().putBoolean(EIP.AUTHED_EIP, authed_eip).commit();
- Intent updateEIP = new Intent(getApplicationContext(), EIP.class);
- updateEIP.setAction(EIP.ACTION_UPDATE_EIP_SERVICE);
- startService(updateEIP);
- buildDashboard(false);
- invalidateOptionsMenu();
- if(data != null && data.hasExtra(LogInDialog.VERB)) {
- View view = ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
- logInDialog(Bundle.EMPTY);
- }
- } else if(resultCode == RESULT_CANCELED && (data == null || data.hasExtra(ACTION_QUIT))) {
- finish();
- } else
- configErrorDialog();
- }
+ @SuppressLint("CommitPrefEdits")
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ Log.d(TAG, "onActivityResult: requestCode = " + requestCode);
+ if ( requestCode == CONFIGURE_LEAP || requestCode == SWITCH_PROVIDER) {
+ if ( resultCode == RESULT_OK ) {
+ preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
+ updateEipService();
+
+ if (data.hasExtra(Provider.KEY)) {
+ provider = data.getParcelableExtra(Provider.KEY);
+ preferences.edit().putBoolean(Constants.PROVIDER_CONFIGURED, true).commit();
+ preferences.edit().putString(Provider.MAIN_URL, provider.mainUrl().toString()).apply();
+ preferences.edit().putString(Provider.KEY, provider.definition().toString()).apply();
+ }
+ buildDashboard(false);
+ invalidateOptionsMenu();
+ if (data.hasExtra(SessionDialog.TAG)) {
+ logInDialog(Bundle.EMPTY);
+ }
+ } else if (resultCode == RESULT_CANCELED && data.hasExtra(ACTION_QUIT)) {
+ finish();
+ } else
+ configErrorDialog();
+ } else if(requestCode == EIP.DISCONNECT) {
+ EipStatus.getInstance().setConnectedOrDisconnected();
}
+ }
/**
* Dialog shown when encountering a configuration error. Such errors require
@@ -172,7 +202,7 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
.setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- preferences.edit().remove(Provider.KEY).commit();
+ preferences.edit().remove(Provider.KEY).remove(Constants.PROVIDER_CONFIGURED).apply();
finish();
}
})
@@ -184,59 +214,59 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
* service dependent UI elements to include.
*/
private void buildDashboard(boolean hide_and_turn_on_eip) {
- provider = Provider.getInstance();
- provider.init( this );
-
- setContentView(R.layout.client_dashboard);
-
- providerNameTV = (TextView) findViewById(R.id.providerName);
- providerNameTV.setText(provider.getDomain());
- providerNameTV.setTextSize(28);
-
- mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
+ setContentView(R.layout.dashboard);
+ ButterKnife.inject(this);
- FragmentManager fragMan = getFragmentManager();
+ provider_name.setText(provider.getDomain());
if ( provider.hasEIP()){
- eipFragment = new EipServiceFragment();
- if (hide_and_turn_on_eip) {
- preferences.edit().remove(Dashboard.START_ON_BOOT).commit();
- Bundle arguments = new Bundle();
- arguments.putBoolean(EipServiceFragment.START_ON_BOOT, true);
- eipFragment.setArguments(arguments);
- }
- fragMan.beginTransaction().replace(R.id.servicesCollection, eipFragment, EipServiceFragment.TAG).commit();
- if (hide_and_turn_on_eip) {
- onBackPressed();
- }
+ fragment_manager.removePreviousFragment(EipServiceFragment.TAG);
+ eip_fragment = new EipServiceFragment();
+
+ if (hide_and_turn_on_eip) {
+ preferences.edit().remove(Dashboard.START_ON_BOOT).apply();
+ Bundle arguments = new Bundle();
+ arguments.putBoolean(EipServiceFragment.START_ON_BOOT, true);
+ if(eip_fragment != null) eip_fragment.setArguments(arguments);
+ }
+
+ fragment_manager.replace(R.id.servicesCollection, eip_fragment, EipServiceFragment.TAG);
+
+ if (hide_and_turn_on_eip) {
+ onBackPressed();
+ }
}
}
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- JSONObject provider_json;
- try {
- provider_json = new JSONObject(preferences.getString(Provider.KEY, ""));
- JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE);
- boolean authed_eip = preferences.getBoolean(EIP.AUTHED_EIP, false);
- boolean allow_registered_eip = service_description.getBoolean(Provider.ALLOW_REGISTRATION);
- preferences.edit().putBoolean(EIP.ALLOWED_REGISTERED, allow_registered_eip);
- if(allow_registered_eip) {
- 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);
- }
- menu.findItem(R.id.signup_button).setVisible(true);
- }
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return true;
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ JSONObject provider_json;
+ try {
+ String provider_json_string = preferences.getString(Provider.KEY, "");
+ if(!provider_json_string.isEmpty()) {
+ provider_json = new JSONObject(provider_json_string);
+ JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE);
+ boolean authed_eip = !LeapSRPSession.getToken().isEmpty();
+ boolean allow_registered_eip = service_description.getBoolean(Provider.ALLOW_REGISTRATION);
+ preferences.edit().putBoolean(Constants.ALLOWED_REGISTERED, allow_registered_eip).apply();
+
+ if(allow_registered_eip) {
+ 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);
+ }
+ menu.findItem(R.id.signup_button).setVisible(true);
+ }
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
}
+ return true;
+ }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
@@ -258,24 +288,23 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
startActivity(startLW);
return true;
case R.id.switch_provider:
- if (Provider.getInstance().hasEIP()){
- if (preferences.getBoolean(EIP.AUTHED_EIP, false)){
- logOut();
- }
- eipStop();
+ if (provider.hasEIP()){
+ if (preferences.getBoolean(Constants.AUTHED_EIP, false)) {
+ logOut();
+ }
+ eip_fragment.stopEIP();
}
- preferences.edit().clear().commit();
+ preferences.edit().clear().apply();
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(Bundle.EMPTY);
return true;
case R.id.logout_button:
logOut();
return true;
case R.id.signup_button:
- signUpDialog(((ViewGroup)findViewById(android.R.id.content)).getChildAt(0), Bundle.EMPTY);
+ signUpDialog(Bundle.EMPTY);
return true;
default:
return super.onOptionsItemSelected(item);
@@ -283,164 +312,100 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
}
- @Override
- public void authenticate(String username, String password) {
- mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
- eipStatus = (TextView) findViewById(R.id.eipStatus);
+ private Intent prepareProviderAPICommand() {
+ providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ providerAPI_result_receiver.setReceiver(this);
- providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
- providerAPI_result_receiver.setReceiver(this);
-
- Intent provider_API_command = new Intent(this, ProviderAPI.class);
+ Intent 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(R.string.authenticating_message);
- //mProgressBar.setMax(4);
- startService(provider_API_command);
- }
+ command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
+ return command;
+ }
+
+ /**
+ * Shows the log in dialog.
+ */
+ public void logInDialog(Bundle resultData) {
+ FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG);
+
+ DialogFragment newFragment = SessionDialog.newInstance();
+ if(resultData != null && !resultData.isEmpty())
+ newFragment.setArguments(resultData);
+ newFragment.show(transaction, SessionDialog.TAG);
+ }
- public void cancelAuthedEipOn() {
- EipServiceFragment eipFragment = (EipServiceFragment) getFragmentManager().findFragmentByTag(EipServiceFragment.TAG);
- eipFragment.checkEipSwitch(false);
+ @Override
+ public void logIn(String username, String password) {
+ Intent provider_API_command = prepareProviderAPICommand();
+ Bundle parameters = provider_API_command.getExtras().getBundle(ProviderAPI.PARAMETERS);
+ if(parameters == null)
+ parameters = new Bundle();
+
+ parameters.putString(SessionDialog.USERNAME, username);
+ parameters.putString(SessionDialog.PASSWORD, password);
+
+ if(eip_fragment != null) {
+ eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
+ eip_fragment.status_message.setText(R.string.authenticating_message);
+ }
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.setAction(ProviderAPI.SRP_AUTH);
+ startService(provider_API_command);
}
public void cancelLoginOrSignup() {
- if(mProgressBar == null) mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
- if(mProgressBar != null) {
- mProgressBar.setVisibility(ProgressBar.GONE);
- if(eipStatus == null) eipStatus = (TextView) findViewById(R.id.eipStatus);
- if(eipStatus != null) eipStatus.setText("");
- }
- cancelAuthedEipOn();
+ EipStatus.getInstance().setConnectedOrDisconnected();
}
- /**
- * 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(R.string.logout_message);
- // eipStatus.setText("Starting to logout");
-
- startService(provider_API_command);
- //mProgressBar.setMax(1);
-
- }
-
- /**
- * Shows the log in dialog.
- */
- public void logInDialog(Bundle resultData) {
- Log.d("Dashboard", "Log In Dialog");
- 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);
- }
-
- @Override
- public void signUp(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(SignUpDialog.USERNAME, username);
- parameters.putString(SignUpDialog.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_REGISTER);
- provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
-
- mProgressBar.setVisibility(ProgressBar.VISIBLE);
- eipStatus.setText(R.string.signingup_message);
- //mProgressBar.setMax(4);
- startService(provider_API_command);
- }
+ /**
+ * Asks ProviderAPI to log out.
+ */
+ public void logOut() {
+ Intent provider_API_command = prepareProviderAPICommand();
+ if(eip_fragment != null) {
+
+ eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
+ eip_fragment.status_message.setText(R.string.logout_message);
+ }
+ provider_API_command.setAction(ProviderAPI.LOG_OUT);
+ startService(provider_API_command);
+ }
- /**
- * Shows the sign up dialog.
- * @param view from which the dialog is created.
- */
- public void signUpDialog(View view, Bundle resultData) {
- FragmentTransaction fragment_transaction = getFragmentManager().beginTransaction();
- Fragment previous_sign_up_dialog = getFragmentManager().findFragmentByTag(SignUpDialog.TAG);
- if (previous_sign_up_dialog != null) {
- fragment_transaction.remove(previous_sign_up_dialog);
- }
- fragment_transaction.addToBackStack(null);
-
- DialogFragment newFragment = SignUpDialog.newInstance();
- if(resultData != null && !resultData.isEmpty()) {
- newFragment.setArguments(resultData);
- }
- newFragment.show(fragment_transaction, SignUpDialog.TAG);
+ /**
+ * Shows the sign up dialog.
+ */
+ public void signUpDialog(Bundle resultData) {
+ FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG);
+
+ DialogFragment newFragment = SessionDialog.newInstance();
+ if(resultData != null && !resultData.isEmpty()) {
+ newFragment.setArguments(resultData);
}
+ newFragment.show(transaction, SessionDialog.TAG);
+ }
+
+ @Override
+ public void signUp(String username, String password) {
+ Intent provider_API_command = prepareProviderAPICommand();
+ Bundle parameters = provider_API_command.getExtras().getBundle(ProviderAPI.PARAMETERS);
+ if(parameters == null)
+ parameters = new Bundle();
+
+ parameters.putString(SessionDialog.USERNAME, username);
+ parameters.putString(SessionDialog.PASSWORD, password);
+ if(eip_fragment != null) {
+ eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
+ eip_fragment.status_message.setText(R.string.signingup_message);
+ }
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.setAction(ProviderAPI.SRP_REGISTER);
+ startService(provider_API_command);
+ }
/**
* 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*/) {
+ private void downloadAuthedUserCertificate() {
providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
providerAPI_result_receiver.setReceiver(this);
@@ -448,8 +413,6 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
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);
@@ -460,71 +423,92 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
- if(resultCode == ProviderAPI.SRP_REGISTRATION_SUCCESSFUL){
- authenticate(resultData.getString(LogInDialog.USERNAME), resultData.getString(LogInDialog.PASSWORD));
- } else if(resultCode == ProviderAPI.SRP_REGISTRATION_FAILED){
- signUpDialog(((ViewGroup)findViewById(android.R.id.content)).getChildAt(0), resultData);
- } else 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;
- preferences.edit().putBoolean(EIP.AUTHED_EIP, authed_eip).commit();
-
- invalidateOptionsMenu();
- mProgressBar.setVisibility(ProgressBar.GONE);
- changeStatusMessage(resultCode);
+ Log.d(TAG, "onReceiveResult");
+ if(resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) {
+ String username = resultData.getString(SessionDialog.USERNAME);
+ String password = resultData.getString(SessionDialog.PASSWORD);
+ logIn(username, password);
+ } else if(resultCode == ProviderAPI.FAILED_SIGNUP) {
+ changeStatusMessage(resultCode);
+ hideProgressBar();
+
+ signUpDialog(resultData);
+ } else if(resultCode == ProviderAPI.SUCCESSFUL_LOGIN) {
+ changeStatusMessage(resultCode);
+ hideProgressBar();
+
+ invalidateOptionsMenu();
+
+ authed_eip = true;
+ preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
+
+ downloadAuthedUserCertificate();
+ } else if(resultCode == ProviderAPI.FAILED_LOGIN) {
+ changeStatusMessage(resultCode);
+ hideProgressBar();
+
+ logInDialog(resultData);
+ } else if(resultCode == ProviderAPI.SUCCESSFUL_LOGOUT) {
+ changeStatusMessage(resultCode);
+ hideProgressBar();
+
+ invalidateOptionsMenu();
+
+ authed_eip = false;
+ preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
- //Cookie session_id = new BasicClientCookie(session_id_cookie_key, session_id_string);
- downloadAuthedUserCertificate(/*session_id*/);
- } else if(resultCode == ProviderAPI.SRP_AUTHENTICATION_FAILED) {
- logInDialog(resultData);
- } else if(resultCode == ProviderAPI.LOGOUT_SUCCESSFUL) {
- authed_eip = false;
- preferences.edit().putBoolean(EIP.AUTHED_EIP, authed_eip).commit();
- mProgressBar.setVisibility(ProgressBar.GONE);
- mProgressBar.setProgress(0);
- invalidateOptionsMenu();
- setResult(RESULT_OK);
- changeStatusMessage(resultCode);
-
- } else if(resultCode == ProviderAPI.LOGOUT_FAILED) {
- setResult(RESULT_CANCELED);
- changeStatusMessage(resultCode);
- mProgressBar.setVisibility(ProgressBar.GONE);
- } else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) {
+ } else if(resultCode == ProviderAPI.LOGOUT_FAILED) {
+ changeStatusMessage(resultCode);
+ hideProgressBar();
+
+ setResult(RESULT_CANCELED);
+ } else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) {
+ changeStatusMessage(resultCode);
+ hideProgressBar();
+
setResult(RESULT_OK);
+
+ updateEipService();
+ } else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
changeStatusMessage(resultCode);
- if(mProgressBar != null)
- mProgressBar.setVisibility(ProgressBar.GONE);
- if(EipServiceFragment.isEipSwitchChecked())
- eipStart();
- else
- eipStatus.setText(R.string.eip_state_not_connected);
- } else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
+ hideProgressBar();
setResult(RESULT_CANCELED);
- changeStatusMessage(resultCode);
- mProgressBar.setVisibility(ProgressBar.GONE);
- }
+ }
}
+ private void updateEipService() {
+ Intent updateEIP = new Intent(getApplicationContext(), EIP.class);
+ updateEIP.setAction(Constants.ACTION_UPDATE_EIP_SERVICE);
+ ResultReceiver receiver = new ResultReceiver(new Handler()) {
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ String request = resultData.getString(Constants.REQUEST_TAG);
+ if(request.equalsIgnoreCase(Constants.ACTION_UPDATE_EIP_SERVICE)) {
+ if(resultCode == Activity.RESULT_OK) {
+ if(authed_eip && eip_fragment != null) eip_fragment.startEipFromScratch();
+ }
+ }
+ }
+ };
+ //updateEIP.putExtra(Constants.RECEIVER_TAG, receiver);
+ startService(updateEIP);
+ }
+
private void changeStatusMessage(final int previous_result_code) {
// TODO Auto-generated method stub
ResultReceiver eip_status_receiver = new ResultReceiver(new Handler()){
protected void onReceiveResult(int resultCode, Bundle resultData){
super.onReceiveResult(resultCode, resultData);
- String request = resultData.getString(EIP.REQUEST_TAG);
- if (request.equalsIgnoreCase(EIP.ACTION_IS_EIP_RUNNING)){
+ String request = resultData.getString(Constants.REQUEST_TAG);
+ if (request.equalsIgnoreCase(Constants.ACTION_IS_EIP_RUNNING)){
if (resultCode == Activity.RESULT_OK){
switch(previous_result_code){
- case ProviderAPI.SRP_AUTHENTICATION_SUCCESSFUL: eipStatus.setText(R.string.succesful_authentication_message); break;
- case ProviderAPI.SRP_AUTHENTICATION_FAILED: eipStatus.setText(R.string.authentication_failed_message); break;
- case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: eipStatus.setText(R.string.authed_secured_status); break;
- case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: eipStatus.setText(R.string.incorrectly_downloaded_certificate_message); break;
- case ProviderAPI.LOGOUT_SUCCESSFUL: eipStatus.setText(R.string.logged_out_message); break;
- case ProviderAPI.LOGOUT_FAILED: eipStatus.setText(R.string.log_out_failed_message); break;
+ case ProviderAPI.SUCCESSFUL_LOGIN: eip_fragment.status_message.setText(R.string.succesful_authentication_message); break;
+ case ProviderAPI.FAILED_LOGIN: eip_fragment.status_message.setText(R.string.authentication_failed_message); break;
+ case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: eip_fragment.status_message.setText(R.string.authed_secured_status); break;
+ case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: eip_fragment.status_message.setText(R.string.incorrectly_downloaded_certificate_message); break;
+ case ProviderAPI.SUCCESSFUL_LOGOUT: eip_fragment.status_message.setText(R.string.logged_out_message); break;
+ case ProviderAPI.LOGOUT_FAILED: eip_fragment.status_message.setText(R.string.log_out_failed_message); break;
}
}
@@ -532,12 +516,13 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
switch(previous_result_code){
- case ProviderAPI.SRP_AUTHENTICATION_SUCCESSFUL: eipStatus.setText(R.string.succesful_authentication_message); break;
- case ProviderAPI.SRP_AUTHENTICATION_FAILED: eipStatus.setText(R.string.authentication_failed_message); break;
+ case ProviderAPI.SUCCESSFUL_LOGIN: eip_fragment.status_message.setText(R.string.succesful_authentication_message); break;
+ case ProviderAPI.FAILED_LOGIN: eip_fragment.status_message.setText(R.string.authentication_failed_message); break;
+ case ProviderAPI.FAILED_SIGNUP: eip_fragment.status_message.setText(R.string.registration_failed_message); break;
case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: break;
- case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: eipStatus.setText(R.string.incorrectly_downloaded_certificate_message); break;
- case ProviderAPI.LOGOUT_SUCCESSFUL: eipStatus.setText(R.string.logged_out_message); break;
- case ProviderAPI.LOGOUT_FAILED: eipStatus.setText(R.string.log_out_failed_message); break;
+ case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: eip_fragment.status_message.setText(R.string.incorrectly_downloaded_certificate_message); break;
+ case ProviderAPI.SUCCESSFUL_LOGOUT: eip_fragment.status_message.setText(R.string.logged_out_message); break;
+ case ProviderAPI.LOGOUT_FAILED: eip_fragment.status_message.setText(R.string.log_out_failed_message); break;
}
}
}
@@ -547,6 +532,13 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
eipIsRunning(eip_status_receiver);
}
+ private void hideProgressBar() {
+ if(eip_fragment != null) {
+ eip_fragment.progress_bar.setProgress(0);
+ eip_fragment.progress_bar.setVisibility(ProgressBar.GONE);
+ }
+ }
+
/**
* For retrieving the base application Context in classes that don't extend
* Android's Activity class
@@ -558,49 +550,17 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
}
- @Override
+ @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);fi
+ eip_intent.setAction(Constants.ACTION_IS_EIP_RUNNING);
+ eip_intent.putExtra(Constants.RECEIVER_TAG, eip_receiver);
startService(eip_intent);
-
}
-
- private void eipStart(){
- Intent eip_intent = new Intent(this, EIP.class);
- eip_intent.setAction(EIP.ACTION_START_EIP);
- eip_intent.putExtra(EIP.RECEIVER_TAG, EipServiceFragment.getReceiver());
- startService(eip_intent);
-
- }
-
- protected void setProgressBarVisibility(int visibility) {
- if(mProgressBar == null)
- mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
- mProgressBar.setVisibility(visibility);
- }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java
index f78002b0..a44253c6 100644
--- a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java
@@ -16,9 +16,6 @@
*/
package se.leap.bitmaskclient;
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.NewProviderDialog.NewProviderDialogInterface;
-import se.leap.bitmaskclient.ProviderListContent.ProviderItem;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
diff --git a/app/src/main/java/se/leap/bitmaskclient/EIP.java b/app/src/main/java/se/leap/bitmaskclient/EIP.java
deleted file mode 100644
index 4a8bae46..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/EIP.java
+++ /dev/null
@@ -1,631 +0,0 @@
-/**
- * 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 <http://www.gnu.org/licenses/>.
- */
-package se.leap.bitmaskclient;
-
-import android.app.Activity;
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.util.Log;
-import de.blinkt.openvpn.LaunchVPN;
-import de.blinkt.openvpn.VpnProfile;
-import de.blinkt.openvpn.activities.DisconnectVPN;
-import de.blinkt.openvpn.core.ConfigParser;
-import de.blinkt.openvpn.core.ConfigParser.ConfigParseError;
-import de.blinkt.openvpn.core.ProfileManager;
-import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
-import java.io.IOException;
-import java.io.StringReader;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.Vector;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import se.leap.bitmaskclient.Dashboard;
-import se.leap.bitmaskclient.Provider;
-import se.leap.bitmaskclient.R;
-
-/**
- * EIP is the abstract base class for interacting with and managing the Encrypted
- * Internet Proxy connection. Connections are started, stopped, and queried through
- * this IntentService.
- * Contains logic for parsing eip-service.json from the provider, configuring and selecting
- * gateways, and controlling {@link de.blinkt.openvpn.core.OpenVPNService} connections.
- *
- * @author Sean Leonard <meanderingcode@aetherislands.net>
- * @author Parménides GV <parmegv@sdf.org>
- */
-public final class EIP extends IntentService {
-
- public final static String AUTHED_EIP = "authed eip";
- public final static String ACTION_CHECK_CERT_VALIDITY = "se.leap.bitmaskclient.CHECK_CERT_VALIDITY";
- public final static String ACTION_START_EIP = "se.leap.bitmaskclient.START_EIP";
- public final static String ACTION_STOP_EIP = "se.leap.bitmaskclient.STOP_EIP";
- public final static String ACTION_UPDATE_EIP_SERVICE = "se.leap.bitmaskclient.UPDATE_EIP_SERVICE";
- public final static String ACTION_IS_EIP_RUNNING = "se.leap.bitmaskclient.IS_RUNNING";
- public final static String ACTION_REBUILD_PROFILES = "se.leap.bitmaskclient.REBUILD_PROFILES";
- public final static String EIP_NOTIFICATION = "EIP_NOTIFICATION";
- public final static String STATUS = "eip status";
- public final static String DATE_FROM_CERTIFICATE = "date from certificate";
- public final static String ALLOWED_ANON = "allow_anonymous";
- public final static String ALLOWED_REGISTERED = "allow_registration";
- public final static String CERTIFICATE = "cert";
- public final static String PRIVATE_KEY = "private_key";
- public final static String KEY = "eip";
- public final static String PARSED_SERIAL = "eip_parsed_serial";
- public final static String SERVICE_API_PATH = "config/eip-service.json";
- public final static String RECEIVER_TAG = "receiverTag";
- public final static String REQUEST_TAG = "requestTag";
- public final static String TAG = "se.leap.bitmaskclient.EIP";
-
- public final static SimpleDateFormat certificate_date_format = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
-
- private static Context context;
- private static ResultReceiver mReceiver;
- private static boolean mBound = false;
-
- private static int parsedEipSerial;
- private static JSONObject eipDefinition = null;
-
- private static OVPNGateway activeGateway = null;
-
- protected static ConnectionStatus lastConnectionStatusLevel;
- protected static boolean mIsDisconnecting = false;
- protected static boolean mIsStarting = false;
-
- public EIP(){
- super("LEAPEIP");
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
-
- context = getApplicationContext();
-
- updateEIPService();
- }
-
- @Override
- public void onDestroy() {
-
- mBound = false;
-
- super.onDestroy();
- }
-
-
- @Override
- protected void onHandleIntent(Intent intent) {
- String action = intent.getAction();
- mReceiver = intent.getParcelableExtra(RECEIVER_TAG);
-
- if ( action == ACTION_START_EIP )
- startEIP();
- else if ( action == ACTION_STOP_EIP )
- stopEIP();
- else if ( action == ACTION_IS_EIP_RUNNING )
- isRunning();
- else if ( action == ACTION_UPDATE_EIP_SERVICE )
- updateEIPService();
- else if ( action == ACTION_CHECK_CERT_VALIDITY )
- checkCertValidity();
- else if ( action == ACTION_REBUILD_PROFILES )
- updateGateways();
- }
-
- /**
- * Initiates an EIP connection by selecting a gateway and preparing and sending an
- * Intent to {@link se.leap.openvpn.LaunchVPN}.
- * It also sets up early routes.
- */
- private void startEIP() {
- earlyRoutes();
- activeGateway = selectGateway();
-
- if(activeGateway != null && activeGateway.mVpnProfile != null) {
- launchActiveGateway();
- }
- }
-
- /**
- * Early routes are routes that block traffic until a new
- * VpnService is started properly.
- */
- private void earlyRoutes() {
- Intent void_vpn_launcher = new Intent(context, VoidVpnLauncher.class);
- void_vpn_launcher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(void_vpn_launcher);
- }
-
- /**
- * Choose a gateway to connect to based on timezone from system locale data
- *
- * @return The gateway to connect to
- */
- private OVPNGateway selectGateway() {
- String closest_location = closestGateway();
- String chosen_host = chooseHost(closest_location);
-
- return new OVPNGateway(chosen_host);
- }
-
- private String closestGateway() {
- TreeMap<Integer, Set<String>> offsets = calculateOffsets();
- return offsets.isEmpty() ? "" : offsets.firstEntry().getValue().iterator().next();
- }
-
- private TreeMap<Integer, Set<String>> calculateOffsets() {
- TreeMap<Integer, Set<String>> offsets = new TreeMap<Integer, Set<String>>();
-
- int localOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
-
- JSONObject locations = availableLocations();
- Iterator<String> locations_names = locations.keys();
- while(locations_names.hasNext()) {
- try {
- String location_name = locations_names.next();
- JSONObject location = locations.getJSONObject(location_name);
-
- int dist = timezoneDistance(localOffset, location.optInt("timezone"));
-
- Set<String> set = (offsets.get(dist) != null) ?
- offsets.get(dist) : new HashSet<String>();
-
- set.add(location_name);
- offsets.put(dist, set);
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- return offsets;
- }
-
- private JSONObject availableLocations() {
- JSONObject locations = null;
- try {
- if(eipDefinition == null) updateEIPService();
- locations = eipDefinition.getJSONObject("locations");
- } catch (JSONException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
-
- return locations;
- }
-
- private int timezoneDistance(int local_timezone, int remote_timezone) {
- // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
- int dist = Math.abs(local_timezone - remote_timezone);
-
- // Farther than 12 timezones and it's shorter around the "back"
- if (dist > 12)
- dist = 12 - (dist -12); // Well i'll be. Absolute values make equations do funny things.
-
- return dist;
- }
-
- private String chooseHost(String location) {
- String chosen_host = "";
- try {
- JSONArray gateways = eipDefinition.getJSONArray("gateways");
- for (int i = 0; i < gateways.length(); i++) {
- JSONObject gw = gateways.getJSONObject(i);
- if ( gw.getString("location").equalsIgnoreCase(location) || location.isEmpty()){
- chosen_host = eipDefinition.getJSONObject("locations").getJSONObject(gw.getString("location")).getString("name");
- break;
- }
- }
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return chosen_host;
- }
-
- private void launchActiveGateway() {
- Intent intent = new Intent(this,LaunchVPN.class);
- intent.setAction(Intent.ACTION_MAIN);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(LaunchVPN.EXTRA_KEY, activeGateway.mVpnProfile.getUUID().toString() );
- intent.putExtra(LaunchVPN.EXTRA_NAME, activeGateway.mVpnProfile.getName() );
- intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
- intent.putExtra(RECEIVER_TAG, mReceiver);
- startActivity(intent);
- }
-
- /**
- * Disconnects the EIP connection gracefully through the bound service or forcefully
- * if there is no bound service. Sends a message to the requesting ResultReceiver.
- */
- private void stopEIP() {
- if(isConnected()) {
- Intent disconnect_vpn = new Intent(this, DisconnectVPN.class);
- disconnect_vpn.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(disconnect_vpn);
- mIsDisconnecting = true;
- lastConnectionStatusLevel = ConnectionStatus.UNKNOWN_LEVEL; // Wait for the decision of the user
- Log.d(TAG, "mIsDisconnecting = true");
- }
-
- tellToReceiver(ACTION_STOP_EIP, Activity.RESULT_OK);
- }
-
- private void tellToReceiver(String action, int resultCode) {
- if (mReceiver != null){
- Bundle resultData = new Bundle();
- resultData.putString(REQUEST_TAG, action);
- mReceiver.send(resultCode, resultData);
- }
- }
-
- /**
- * Checks the last stored status notified by ics-openvpn
- * Sends <code>Activity.RESULT_CANCELED</code> to the ResultReceiver that made the
- * request if it's not connected, <code>Activity.RESULT_OK</code> otherwise.
- */
-
- private void isRunning() {
- int resultCode = Activity.RESULT_CANCELED;
- boolean is_connected = isConnected();
-
- resultCode = (is_connected) ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
-
- tellToReceiver(ACTION_IS_EIP_RUNNING, resultCode);
- }
-
- protected static boolean isConnected() {
- return lastConnectionStatusLevel != null && lastConnectionStatusLevel.equals(ConnectionStatus.LEVEL_CONNECTED) && !mIsDisconnecting;
- }
-
- /**
- * Loads eip-service.json from SharedPreferences and calls {@link updateGateways()}
- * to parse gateway definitions.
- * TODO Implement API call to refresh eip-service.json from the provider
- */
- private void updateEIPService() {
- try {
- eipDefinition = new JSONObject(getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).getString(KEY, ""));
- parsedEipSerial = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).getInt(PARSED_SERIAL, 0);
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- if(parsedEipSerial == 0) {
- deleteAllVpnProfiles();
- }
- if (eipDefinition != null && eipDefinition.optInt("serial") > parsedEipSerial)
- updateGateways();
- }
-
- private void deleteAllVpnProfiles() {
- ProfileManager vpl = ProfileManager.getInstance(context);
- VpnProfile[] profiles = (VpnProfile[]) vpl.getProfiles().toArray(new VpnProfile[vpl.getProfiles().size()]);
- for (int current_profile = 0; current_profile < profiles.length; current_profile++){
- vpl.removeProfile(context, profiles[current_profile]);
- }
- }
-
- /**
- * Walk the list of gateways defined in eip-service.json and parse them into
- * OVPNGateway objects.
- * TODO Store the OVPNGateways (as Serializable) in SharedPreferences
- */
- private void updateGateways(){
- JSONArray gatewaysDefined = null;
-
- try {
- gatewaysDefined = eipDefinition.getJSONArray("gateways");
- for ( int i=0 ; i < gatewaysDefined.length(); i++ ){
- JSONObject gw = null;
- gw = gatewaysDefined.getJSONObject(i);
-
- if ( gw.getJSONObject("capabilities").getJSONArray("transport").toString().contains("openvpn") )
- new OVPNGateway(gw);
- }
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).edit().putInt(PARSED_SERIAL, eipDefinition.optInt(Provider.API_RETURN_SERIAL)).commit();
- }
-
- private void checkCertValidity() {
- String certificate_string = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).getString(CERTIFICATE, "");
- if(!certificate_string.isEmpty()) {
- String date_from_certificate_string = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).getString(DATE_FROM_CERTIFICATE, certificate_date_format.format(Calendar.getInstance().getTime()).toString());
- X509Certificate certificate_x509 = ConfigHelper.parseX509CertificateFromString(certificate_string);
-
- Calendar offset_date = Calendar.getInstance();
- try {
- Date date_from_certificate = certificate_date_format.parse(date_from_certificate_string);
- long difference = Math.abs(date_from_certificate.getTime() - certificate_x509.getNotAfter().getTime())/2;
- long current_date_millis = offset_date.getTimeInMillis();
- offset_date.setTimeInMillis(current_date_millis + difference);
- Log.d(TAG, "certificate not after = " + certificate_x509.getNotAfter());
- } catch(ParseException e) {
- e.printStackTrace();
- }
-
- Bundle result_data = new Bundle();
- result_data.putString(REQUEST_TAG, ACTION_CHECK_CERT_VALIDITY);
- try {
- Log.d(TAG, "offset_date = " + offset_date.getTime().toString());
- certificate_x509.checkValidity(offset_date.getTime());
- mReceiver.send(Activity.RESULT_OK, result_data);
- Log.d(TAG, "Valid certificate");
- } catch(CertificateExpiredException e) {
- mReceiver.send(Activity.RESULT_CANCELED, result_data);
- Log.d(TAG, "Updating certificate");
- } catch(CertificateNotYetValidException e) {
- mReceiver.send(Activity.RESULT_CANCELED, result_data);
- }
- }
- }
-
- /**
- * OVPNGateway provides objects defining gateways and their options and metadata.
- * Each instance contains a VpnProfile for OpenVPN specific data and member
- * variables describing capabilities and location
- *
- * @author Sean Leonard <meanderingcode@aetherislands.net>
- */
- private class OVPNGateway {
-
- private String TAG = "OVPNGateway";
-
- private String mName;
- private VpnProfile mVpnProfile;
- private JSONObject mGateway;
- private HashMap<String,Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>();
-
-
- /**
- * Attempts to retrieve a VpnProfile by name and build an OVPNGateway around it.
- * FIXME This needs to become a findGatewayByName() method
- *
- * @param name The hostname of the gateway to inflate
- */
- private OVPNGateway(String name){
- mName = name;
-
- this.loadVpnProfile();
- }
-
- private void loadVpnProfile() {
- ProfileManager vpl = ProfileManager.getInstance(context);
- try {
- if ( mName == null )
- mVpnProfile = vpl.getProfiles().iterator().next();
- else
- mVpnProfile = vpl.getProfileByName(mName);
- } catch (NoSuchElementException e) {
- updateEIPService();
- this.loadVpnProfile(); // FIXME catch infinite loops
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- /**
- * Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json
- * and create a VpnProfile belonging to it.
- *
- * @param gateway The JSON OpenVPN gateway definition to parse
- */
- protected OVPNGateway(JSONObject gateway){
-
- mGateway = gateway;
-
- // Currently deletes VpnProfile for host, if there already is one, and builds new
- ProfileManager vpl = ProfileManager.getInstance(context);
- Collection<VpnProfile> profiles = vpl.getProfiles();
- for (Iterator<VpnProfile> it = profiles.iterator(); it.hasNext(); ){
- VpnProfile p = it.next();
-
- if ( p.mName.equalsIgnoreCase( mName ) ) {
- it.remove();
- vpl.removeProfile(context, p);
- }
- }
-
- this.createVPNProfile();
-
- vpl.addProfile(mVpnProfile);
- vpl.saveProfile(context, mVpnProfile);
- vpl.saveProfileList(context);
- }
-
- /**
- * Create and attach the VpnProfile to our gateway object
- */
- protected void createVPNProfile(){
- try {
- ConfigParser cp = new ConfigParser();
- cp.parseConfig(new StringReader(configFromEipServiceDotJson()));
- cp.parseConfig(new StringReader(caSecretFromSharedPreferences()));
- cp.parseConfig(new StringReader(keySecretFromSharedPreferences()));
- cp.parseConfig(new StringReader(certSecretFromSharedPreferences()));
- cp.parseConfig(new StringReader("remote-cert-tls server"));
- cp.parseConfig(new StringReader("persist-tun"));
- VpnProfile vp = cp.convertProfile();
- //vp.mAuthenticationType=VpnProfile.TYPE_STATICKEYS;
- mVpnProfile = vp;
- mVpnProfile.mName = mName = locationAsName();
- Log.v(TAG,"Created VPNProfile");
- } catch (ConfigParseError e) {
- // FIXME We didn't get a VpnProfile! Error handling! and log level
- Log.v(TAG,"Error creating VPNProfile");
- e.printStackTrace();
- } catch (IOException e) {
- // FIXME We didn't get a VpnProfile! Error handling! and log level
- Log.v(TAG,"Error creating VPNProfile");
- e.printStackTrace();
- }
- }
-
- /**
- * Parses data from eip-service.json to a section of the openvpn config file
- */
- private String configFromEipServiceDotJson() {
- String parsed_configuration = "";
-
- String location_key = "location";
- String locations = "locations";
-
- parsed_configuration += extractCommonOptionsFromEipServiceDotJson();
- parsed_configuration += extractRemotesFromEipServiceDotJson();
-
- return parsed_configuration;
- }
-
- private String extractCommonOptionsFromEipServiceDotJson() {
- String common_options = "";
- try {
- String common_options_key = "openvpn_configuration";
- JSONObject openvpn_configuration = eipDefinition.getJSONObject(common_options_key);
- Iterator keys = openvpn_configuration.keys();
- Vector<Vector<String>> value = new Vector<Vector<String>>();
- while ( keys.hasNext() ){
- String key = keys.next().toString();
-
- common_options += key + " ";
- for ( String word : openvpn_configuration.getString(key).split(" ") )
- common_options += word + " ";
- common_options += System.getProperty("line.separator");
-
- }
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- common_options += "client" + System.getProperty("line.separator");
-
- return common_options;
- }
-
-
- private String extractRemotesFromEipServiceDotJson() {
- String remotes = "";
-
- String remote = "ip_address";
- String remote_openvpn_keyword = "remote";
- String ports = "ports";
- String protos = "protocols";
- String capabilities = "capabilities";
- String udp = "udp";
-
- try {
- JSONArray protocolsJSON = mGateway.getJSONObject(capabilities).getJSONArray(protos);
- for ( int i=0; i<protocolsJSON.length(); i++ ) {
- String remote_line = remote_openvpn_keyword;
- remote_line += " " + mGateway.getString(remote);
- remote_line += " " + mGateway.getJSONObject(capabilities).getJSONArray(ports).optString(0);
- remote_line += " " + protocolsJSON.optString(i);
- if(remote_line.endsWith(udp))
- remotes = remotes.replaceFirst(remote_openvpn_keyword, remote_line + System.getProperty("line.separator") + remote_openvpn_keyword);
- else
- remotes += remote_line;
- remotes += System.getProperty("line.separator");
- }
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- Log.d(TAG, "remotes = " + remotes);
- return remotes;
- }
-
- private String caSecretFromSharedPreferences() {
- String secret_lines = "";
- SharedPreferences preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, context.MODE_PRIVATE);
-
- System.getProperty("line.separator");
- secret_lines += "<ca>";
- secret_lines += System.getProperty("line.separator");
- secret_lines += preferences.getString(Provider.CA_CERT, "");
- secret_lines += System.getProperty("line.separator");
- secret_lines += "</ca>";
-
- return secret_lines;
- }
-
- private String keySecretFromSharedPreferences() {
- String secret_lines = "";
- SharedPreferences preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, context.MODE_PRIVATE);
-
- secret_lines += System.getProperty("line.separator");
- secret_lines +="<key>";
- secret_lines += System.getProperty("line.separator");
- secret_lines += preferences.getString(EIP.PRIVATE_KEY, "");
- secret_lines += System.getProperty("line.separator");
- secret_lines += "</key>";
- secret_lines += System.getProperty("line.separator");
-
- return secret_lines;
- }
-
- private String certSecretFromSharedPreferences() {
- String secret_lines = "";
- SharedPreferences preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, context.MODE_PRIVATE);
-
- secret_lines += System.getProperty("line.separator");
- secret_lines +="<cert>";
- secret_lines += System.getProperty("line.separator");
- secret_lines += preferences.getString(EIP.CERTIFICATE, "");
- secret_lines += System.getProperty("line.separator");
- secret_lines += "</cert>";
- secret_lines += System.getProperty("line.separator");
-
- return secret_lines;
- }
-
-
- public String locationAsName() {
- try {
- return eipDefinition.getJSONObject("locations").getJSONObject(mGateway.getString("location")).getString("name");
- } catch (JSONException e) {
- Log.v(TAG,"Couldn't read gateway name for profile creation! Returning original name = " + mName);
- e.printStackTrace();
- return (mName != null) ? mName : "";
- }
- }
- }
-}
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
index 18ee0262..1b40c94c 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
@@ -1,14 +1,5 @@
package se.leap.bitmaskclient;
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.ProviderAPIResultReceiver;
-import se.leap.bitmaskclient.ProviderAPIResultReceiver.Receiver;
-import se.leap.bitmaskclient.Dashboard;
-
-import de.blinkt.openvpn.activities.LogWindow;
-import de.blinkt.openvpn.core.VpnStatus;
-import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
-import de.blinkt.openvpn.core.VpnStatus.StateListener;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
@@ -19,107 +10,113 @@ import android.os.Handler;
import android.os.ResultReceiver;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.CompoundButton;
import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
import android.widget.Switch;
import android.widget.TextView;
-public class EipServiceFragment extends Fragment implements StateListener, OnCheckedChangeListener {
+import java.util.Observable;
+import java.util.Observer;
+
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+import butterknife.OnCheckedChanged;
+import de.blinkt.openvpn.activities.DisconnectVPN;
+import se.leap.bitmaskclient.eip.Constants;
+import se.leap.bitmaskclient.eip.EIP;
+import se.leap.bitmaskclient.eip.EipStatus;
+
+public class EipServiceFragment extends Fragment implements Observer {
- protected static final String IS_EIP_PENDING = "is_eip_pending";
+ public static String TAG = "se.leap.bitmask.EipServiceFragment";
+
+ protected static final String IS_PENDING = TAG + ".is_pending";
+ protected static final String IS_CONNECTED = TAG + ".is_connected";
+ protected static final String STATUS_MESSAGE = TAG + ".status_message";
public static final String START_ON_BOOT = "start on boot";
-
- private View eipFragment;
- private static Switch eipSwitch;
- private View eipDetail;
- private TextView eipStatus;
+ private View view;
+ @InjectView(R.id.eipSwitch)
+ Switch eip_switch;
+ @InjectView(R.id.status_message)
+ TextView status_message;
+ @InjectView(R.id.eipProgress)
+ ProgressBar progress_bar;
+
+ private static Activity parent_activity;
private static EIPReceiver mEIPReceiver;
+ private static EipStatus eip_status;
+ private boolean is_starting_to_connect;
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ parent_activity = activity;
+ }
- public static String TAG = "se.leap.bitmask.EipServiceFragment";
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ eip_status = EipStatus.getInstance();
+ eip_status.addObserver(this);
+ mEIPReceiver = new EIPReceiver(new Handler());
+ }
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
-
- eipFragment = inflater.inflate(R.layout.eip_service_fragment, container, false);
- eipDetail = ((RelativeLayout) eipFragment.findViewById(R.id.eipDetail));
- eipDetail.setVisibility(View.VISIBLE);
-
- View eipSettings = eipFragment.findViewById(R.id.eipSettings);
- eipSettings.setVisibility(View.GONE); // FIXME too!
-
- if (EIP.mIsStarting)
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
-
- eipStatus = (TextView) eipFragment.findViewById(R.id.eipStatus);
-
- eipSwitch = (Switch) eipFragment.findViewById(R.id.eipSwitch);
- eipSwitch.setOnCheckedChangeListener(this);
-
- if(getArguments() != null && getArguments().containsKey(START_ON_BOOT) && getArguments().getBoolean(START_ON_BOOT))
- startEipFromScratch();
-
- return eipFragment;
- }
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ view = inflater.inflate(R.layout.eip_service_fragment, container, false);
+ ButterKnife.inject(this, view);
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mEIPReceiver = new EIPReceiver(new Handler());
+ if (eip_status.isConnecting())
+ eip_switch.setVisibility(View.VISIBLE);
- if (savedInstanceState != null)
- EIP.mIsStarting = savedInstanceState.getBoolean(IS_EIP_PENDING);
- }
+ Log.d(TAG, "onCreateView, eip_switch is checked? " + eip_switch.isChecked());
- @Override
- public void onResume() {
- super.onResume();
+ Bundle arguments = getArguments();
+ if(arguments != null && arguments.containsKey(START_ON_BOOT) && arguments.getBoolean(START_ON_BOOT))
+ startEipFromScratch();
- VpnStatus.addStateListener(this);
-
- eipCommand(EIP.ACTION_CHECK_CERT_VALIDITY);
- }
-
- @Override
- public void onPause() {
- super.onPause();
+ if (savedInstanceState != null) {
+ status_message.setText(savedInstanceState.getString(STATUS_MESSAGE));
+ if(savedInstanceState.getBoolean(IS_PENDING))
+ eip_status.setConnecting();
+ else if(savedInstanceState.getBoolean(IS_CONNECTED)) {
+ eip_status.setConnectedOrDisconnected();
+ }
+ }
+ return view;
+ }
- VpnStatus.removeStateListener(this);
- }
+ @Override
+ public void onResume() {
+ super.onResume();
+ eipCommand(Constants.ACTION_CHECK_CERT_VALIDITY);
+ handleNewState(eip_status);
+ }
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(IS_EIP_PENDING, EIP.mIsStarting);
- }
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putBoolean(IS_PENDING, eip_status.isConnecting());
+ outState.putBoolean(IS_CONNECTED, eip_status.isConnected());
+ Log.d(TAG, "status message onSaveInstanceState = " + status_message.getText().toString());
+ outState.putString(STATUS_MESSAGE, status_message.getText().toString());
+ super.onSaveInstanceState(outState);
+ }
protected void saveEipStatus() {
boolean eip_is_on = false;
- Log.d("bitmask", "saveEipStatus");
- if(eipSwitch.isChecked()) {
+ Log.d(TAG, "saveEipStatus");
+ if(eip_switch.isChecked()) {
eip_is_on = true;
}
- if(getActivity() != null)
+ if(parent_activity != null)
Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, eip_is_on).commit();
}
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (buttonView.equals(eipSwitch)){
- handleSwitch(isChecked);
- }
- }
-
- private void handleSwitch(boolean isChecked) {
+
+ @OnCheckedChanged(R.id.eipSwitch)
+ void handleSwitch(boolean isChecked) {
if(isChecked)
handleSwitchOn();
else
@@ -133,284 +130,226 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
startEipFromScratch();
else if(canLogInToStartEIP()) {
Log.d(TAG, "Can Log In to start EIP");
- Dashboard dashboard = (Dashboard) getActivity();
- dashboard.logInDialog(Bundle.EMPTY);
+ Dashboard dashboard = (Dashboard) parent_activity;
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(IS_PENDING, true);
+ dashboard.logInDialog(bundle);
}
}
private boolean canStartEIP() {
- boolean certificateExists = !Dashboard.preferences.getString(EIP.CERTIFICATE, "").isEmpty();
- boolean isAllowedAnon = Dashboard.preferences.getBoolean(EIP.ALLOWED_ANON, false);
- return (isAllowedAnon || certificateExists) && !EIP.mIsStarting && !EIP.isConnected();
+ boolean certificateExists = !Dashboard.preferences.getString(Constants.CERTIFICATE, "").isEmpty();
+ boolean isAllowedAnon = Dashboard.preferences.getBoolean(Constants.ALLOWED_ANON, false);
+ return (isAllowedAnon || certificateExists) && !eip_status.isConnected() && !eip_status.isConnecting();
}
private boolean canLogInToStartEIP() {
- boolean isAllowedRegistered = Dashboard.preferences.getBoolean(EIP.ALLOWED_REGISTERED, false);
- boolean isLoggedIn = Dashboard.preferences.getBoolean(EIP.AUTHED_EIP, false);
+ boolean isAllowedRegistered = Dashboard.preferences.getBoolean(Constants.ALLOWED_REGISTERED, false);
+ boolean isLoggedIn = !LeapSRPSession.getToken().isEmpty();
Log.d(TAG, "Allow registered? " + isAllowedRegistered);
Log.d(TAG, "Is logged in? " + isLoggedIn);
- return isAllowedRegistered && !isLoggedIn && !EIP.mIsStarting && !EIP.isConnected();
+ return isAllowedRegistered && !isLoggedIn && !eip_status.isConnecting() && !eip_status.isConnected();
}
private void handleSwitchOff() {
- if(EIP.mIsStarting) {
+ if(eip_status.isConnecting()) {
askPendingStartCancellation();
- } else if(EIP.isConnected()) {
- Log.d(TAG, "Stopping EIP");
+ } else if(eip_status.isConnected()) {
stopEIP();
}
}
private void askPendingStartCancellation() {
- AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity());
- alertBuilder.setTitle(getResources().getString(R.string.eip_cancel_connect_title))
- .setMessage(getResources().getString(R.string.eip_cancel_connect_text))
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(parent_activity);
+ alertBuilder.setTitle(parent_activity.getString(R.string.eip_cancel_connect_title))
+ .setMessage(parent_activity.getString(R.string.eip_cancel_connect_text))
.setPositiveButton((R.string.yes), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
stopEIP();
}
})
- .setNegativeButton(getResources().getString(R.string.no), new DialogInterface.OnClickListener() {
+ .setNegativeButton(parent_activity.getString(R.string.no), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- Log.d(TAG, "askPendingStartCancellation checks the switch to true");
- eipSwitch.setChecked(true);
+ eip_switch.setChecked(true);
}
})
.show();
}
public void startEipFromScratch() {
- EIP.mIsStarting = true;
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
- String status = getResources().getString(R.string.eip_status_start_pending);
- setEipStatus(status);
+ is_starting_to_connect = true;
+ progress_bar.setVisibility(View.VISIBLE);
+ eip_switch.setVisibility(View.VISIBLE);
+ String status = parent_activity.getString(R.string.eip_status_start_pending);
+ status_message.setText(status);
- if(!eipSwitch.isChecked()) {
- Log.d(TAG, "startEipFromScratch checks the switch to true");
- eipSwitch.setChecked(true);
+ if(!eip_switch.isChecked()) {
+ eip_switch.setChecked(true);
saveEipStatus();
}
- eipCommand(EIP.ACTION_START_EIP);
+ eipCommand(Constants.ACTION_START_EIP);
}
- private void stopEIP() {
- EIP.mIsStarting = false;
- View eipProgressBar = getActivity().findViewById(R.id.eipProgress);
- if(eipProgressBar != null)
- eipProgressBar.setVisibility(View.GONE);
-
- String status = getResources().getString(R.string.eip_state_not_connected);
- setEipStatus(status);
- eipCommand(EIP.ACTION_STOP_EIP);
+ protected void stopEIP() {
+ hideProgressBar();
+
+ String status = parent_activity.getString(R.string.eip_state_not_connected);
+ status_message.setText(status);
+ eipCommand(Constants.ACTION_STOP_EIP);
}
- /**
- * Send a command to EIP
- *
- * @param action A valid String constant from EIP class representing an Intent
- * filter for the EIP class
- */
- private void eipCommand(String action){
- // TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
- Intent vpn_intent = new Intent(getActivity().getApplicationContext(), EIP.class);
- vpn_intent.setAction(action);
- vpn_intent.putExtra(EIP.RECEIVER_TAG, mEIPReceiver);
- getActivity().startService(vpn_intent);
- }
+ /**
+ * Send a command to EIP
+ *
+ * @param action A valid String constant from EIP class representing an Intent
+ * filter for the EIP class
+ */
+ private void eipCommand(String action){
+ // TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
+ Intent vpn_intent = new Intent(parent_activity.getApplicationContext(), EIP.class);
+ vpn_intent.setAction(action);
+ vpn_intent.putExtra(Constants.RECEIVER_TAG, mEIPReceiver);
+ parent_activity.startService(vpn_intent);
+ }
@Override
- public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
- boolean isNewLevel = EIP.lastConnectionStatusLevel != level;
- boolean justDecidedOnDisconnect = EIP.lastConnectionStatusLevel == ConnectionStatus.UNKNOWN_LEVEL;
- Log.d(TAG, "update state with level " + level);
- if(!justDecidedOnDisconnect && (isNewLevel || level == ConnectionStatus.LEVEL_CONNECTED)) {
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- EIP.lastConnectionStatusLevel = level;
- handleNewState(state, logmessage, localizedResId, level);
+ public void update (Observable observable, Object data) {
+ if(observable instanceof EipStatus) {
+ eip_status = (EipStatus) observable;
+ final EipStatus eip_status = (EipStatus) observable;
+ parent_activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ handleNewState(eip_status);
}
});
- } else if(justDecidedOnDisconnect && level == ConnectionStatus.LEVEL_CONNECTED) {
- EIP.lastConnectionStatusLevel = ConnectionStatus.LEVEL_NOTCONNECTED;
- updateState(state, logmessage, localizedResId, level);
}
}
- private void handleNewState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
- if (level == ConnectionStatus.LEVEL_CONNECTED)
+ private void handleNewState(EipStatus eip_status) {
+ Log.d(TAG, "handleNewState: " + eip_status.toString());
+ if(eip_status.wantsToDisconnect())
+ setDisconnectedUI();
+ else if(eip_status.isConnecting() || is_starting_to_connect)
+ setInProgressUI(eip_status);
+ else if (eip_status.isConnected())
setConnectedUI();
- else if (isDisconnectedLevel(level) && !EIP.mIsStarting)
+ else if (eip_status.isDisconnected() && !eip_status.isConnecting())
setDisconnectedUI();
- else if (level == ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET)
- setNoServerReplyUI(localizedResId, logmessage);
- else if (level == ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED)
- setServerReplyUI(state, localizedResId, logmessage);
- }
-
- private boolean isDisconnectedLevel(final ConnectionStatus level) {
- return level == ConnectionStatus.LEVEL_NOTCONNECTED || level == ConnectionStatus.LEVEL_AUTH_FAILED;
}
private void setConnectedUI() {
hideProgressBar();
- Log.d(TAG, "mIsDisconnecting = false in setConnectedUI");
- EIP.mIsStarting = false; //TODO This should be done in the onReceiveResult from START_EIP command, but right now LaunchVPN isn't notifying anybody the resultcode of the request so we need to listen the states with this listener.
- EIP.mIsDisconnecting = false; //TODO See comment above
- String status = getString(R.string.eip_state_connected);
- setEipStatus(status);
+ Log.d(TAG, "setConnectedUi? " + eip_status.isConnected());
adjustSwitch();
+ is_starting_to_connect = false;
+ status_message.setText(parent_activity.getString(R.string.eip_state_connected));
}
private void setDisconnectedUI(){
hideProgressBar();
- EIP.mIsStarting = false; //TODO See comment in setConnectedUI()
- Log.d(TAG, "mIsDisconnecting = false in setDisconnectedUI");
- EIP.mIsDisconnecting = false; //TODO See comment in setConnectedUI()
-
- String status = getString(R.string.eip_state_not_connected);
- setEipStatus(status);
adjustSwitch();
+ status_message.setText(parent_activity.getString(R.string.eip_state_not_connected));
}
- private void adjustSwitch() {
- if(EIP.isConnected()) {
- if(!eipSwitch.isChecked()) {
- eipSwitch.setChecked(true);
+ private void adjustSwitch() {
+ if(eip_status.isConnected() || eip_status.isConnecting() || is_starting_to_connect) {
+ Log.d(TAG, "adjustSwitch, isConnected || isConnecting, is checked");
+ if(!eip_switch.isChecked()) {
+ eip_switch.setChecked(true);
}
} else {
- if(eipSwitch.isChecked()) {
- eipSwitch.setChecked(false);
- }
- }
- }
+ Log.d(TAG, "adjustSwitch, !isConnected && !isConnecting? " + eip_status.toString());
- private void setNoServerReplyUI(int localizedResId, String logmessage) {
- if(eipStatus != null) {
- String prefix = getString(localizedResId);
- setEipStatus(prefix + " " + logmessage);
+ if(eip_switch.isChecked()) {
+ eip_switch.setChecked(false);
+ }
}
}
- private void setServerReplyUI(String state, int localizedResId, String logmessage) {
- if(eipStatus != null)
- if(state.equals("AUTH") || state.equals("GET_CONFIG")) {
- String prefix = getString(localizedResId);
- setEipStatus(prefix + " " + logmessage);
- }
- }
+ private void setInProgressUI(EipStatus eip_status) {
+ int localizedResId = eip_status.getLocalizedResId();
+ String logmessage = eip_status.getLogMessage();
+ String prefix = parent_activity.getString(localizedResId);
- protected void setEipStatus(String status) {
- if(eipStatus == null)
- eipStatus = (TextView) getActivity().findViewById(R.id.eipStatus);
- eipStatus.setText(status);
+ status_message.setText(prefix + " " + logmessage);
+ is_starting_to_connect = false;
+ adjustSwitch();
}
private void hideProgressBar() {
- if(getActivity() != null && getActivity().findViewById(R.id.eipProgress) != null)
- getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE);
+ if(progress_bar != null)
+ progress_bar.setVisibility(View.GONE);
}
- /**
- * Inner class for handling messages related to EIP status and control requests
- *
- * @author Sean Leonard <meanderingcode@aetherislands.net>
- */
- protected class EIPReceiver extends ResultReceiver {
-
- protected EIPReceiver(Handler handler){
- super(handler);
- }
+ protected class EIPReceiver extends ResultReceiver {
+
+ protected EIPReceiver(Handler handler){
+ super(handler);
+ }
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ super.onReceiveResult(resultCode, resultData);
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- super.onReceiveResult(resultCode, resultData);
-
- String request = resultData.getString(EIP.REQUEST_TAG);
- boolean checked = false;
-
- if (request == EIP.ACTION_IS_EIP_RUNNING) {
- switch (resultCode){
- case Activity.RESULT_OK:
- checked = true;
- break;
- case Activity.RESULT_CANCELED:
- checked = false;
- break;
- }
- } else if (request == EIP.ACTION_START_EIP) {
- switch (resultCode){
- case Activity.RESULT_OK:
- Log.d(TAG, "Action start eip = Result OK");
- checked = true;
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
- EIP.mIsStarting = false;
- break;
- case Activity.RESULT_CANCELED:
- checked = false;
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.GONE);
- break;
- }
- } else if (request == EIP.ACTION_STOP_EIP) {
- switch (resultCode){
- case Activity.RESULT_OK:
- checked = false;
- break;
- case Activity.RESULT_CANCELED:
- checked = true;
- break;
- }
- } else if (request == EIP.EIP_NOTIFICATION) {
- switch (resultCode){
- case Activity.RESULT_OK:
- checked = true;
- break;
- case Activity.RESULT_CANCELED:
- checked = false;
- break;
- }
- } else if (request == EIP.ACTION_CHECK_CERT_VALIDITY) {
- checked = eipSwitch.isChecked();
-
- switch (resultCode) {
- case Activity.RESULT_OK:
- break;
- case Activity.RESULT_CANCELED:
- Dashboard dashboard = (Dashboard) getActivity();
-
- dashboard.setProgressBarVisibility(ProgressBar.VISIBLE);
- String status = getResources().getString(R.string.updating_certificate_message);
- setEipStatus(status);
-
- Intent provider_API_command = new Intent(getActivity(), ProviderAPI.class);
- if(dashboard.providerAPI_result_receiver == null) {
- dashboard.providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
- dashboard.providerAPI_result_receiver.setReceiver(dashboard);
- }
-
- provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, dashboard.providerAPI_result_receiver);
- getActivity().startService(provider_API_command);
- break;
- }
+ String request = resultData.getString(Constants.REQUEST_TAG);
+
+ if (request.equals(Constants.ACTION_START_EIP)) {
+ switch (resultCode){
+ case Activity.RESULT_OK:
+ break;
+ case Activity.RESULT_CANCELED:
+ break;
+ }
+ } else if (request.equals(Constants.ACTION_STOP_EIP)) {
+ switch (resultCode){
+ case Activity.RESULT_OK:
+ Intent disconnect_vpn = new Intent(parent_activity, DisconnectVPN.class);
+ parent_activity.startActivityForResult(disconnect_vpn, EIP.DISCONNECT);
+ eip_status.setDisconnecting();
+ break;
+ case Activity.RESULT_CANCELED:
+ break;
+ }
+ } else if (request.equals(Constants.EIP_NOTIFICATION)) {
+ switch (resultCode){
+ case Activity.RESULT_OK:
+ break;
+ case Activity.RESULT_CANCELED:
+ break;
+ }
+ } else if (request.equals(Constants.ACTION_CHECK_CERT_VALIDITY)) {
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ break;
+ case Activity.RESULT_CANCELED:
+ Dashboard dashboard = (Dashboard) parent_activity;
+
+ progress_bar.setVisibility(View.VISIBLE);
+ status_message.setText(getString(R.string.updating_certificate_message));
+ if(LeapSRPSession.getToken().isEmpty() && !Dashboard.preferences.getBoolean(Constants.ALLOWED_ANON, false)) {
+ dashboard.logInDialog(Bundle.EMPTY);
+ } else {
+ Intent provider_API_command = new Intent(parent_activity, ProviderAPI.class);
+ if(dashboard.providerAPI_result_receiver == null) {
+ dashboard.providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ dashboard.providerAPI_result_receiver.setReceiver(dashboard);
}
+
+ provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, dashboard.providerAPI_result_receiver);
+ parent_activity.startService(provider_API_command);
+ }
+ break;
}
+ }
}
-
-
- public static EIPReceiver getReceiver() {
- return mEIPReceiver;
}
- public static boolean isEipSwitchChecked() {
- return eipSwitch.isChecked();
- }
- public void checkEipSwitch(boolean checked) {
- eipSwitch.setChecked(checked);
- // Log.d(TAG, "checkEipSwitch");
- // onCheckedChanged(eipSwitch, checked);
+ public static EIPReceiver getReceiver() {
+ return mEIPReceiver;
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java b/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java
new file mode 100644
index 00000000..49af9274
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java
@@ -0,0 +1,55 @@
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+
+public class FragmentManagerEnhanced {
+
+ private FragmentManager generic_fragment_manager;
+
+ public FragmentManagerEnhanced(FragmentManager generic_fragment_manager) {
+ this.generic_fragment_manager = generic_fragment_manager;
+ }
+
+ public FragmentTransaction removePreviousFragment(String tag) {
+ FragmentTransaction transaction = generic_fragment_manager.beginTransaction();
+ Fragment previous_fragment = generic_fragment_manager.findFragmentByTag(tag);
+ if (previous_fragment != null) {
+ transaction.remove(previous_fragment);
+ }
+ transaction.addToBackStack(null);
+
+ return transaction;
+ }
+
+ public void replace(int containerViewId, Fragment fragment, String tag) {
+ FragmentTransaction transaction = generic_fragment_manager.beginTransaction();
+
+ transaction.replace(containerViewId, fragment, tag).commit();
+ }
+
+ public FragmentTransaction beginTransaction() {
+ return generic_fragment_manager.beginTransaction();
+ }
+
+ public Fragment findFragmentByTag(String tag) {
+ return generic_fragment_manager.findFragmentByTag(tag);
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java b/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java
index a953a710..989dc395 100644
--- a/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java
+++ b/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java
@@ -17,13 +17,14 @@
package se.leap.bitmaskclient;
+import org.jboss.security.srp.SRPParameters;
+
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
-import org.jboss.security.srp.SRPParameters;
/**
* Implements all SRP algorithm logic.
diff --git a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
index eb196d46..07ed6c8f 100644
--- a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
@@ -3,8 +3,8 @@ package se.leap.bitmaskclient;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.util.Log;
+import se.leap.bitmaskclient.eip.Constants;
public class OnBootReceiver extends BroadcastReceiver {
@@ -14,7 +14,7 @@ public class OnBootReceiver extends BroadcastReceiver {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
if (!context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE).getString(Provider.KEY, "").isEmpty() && context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE).getBoolean(Dashboard.START_ON_BOOT, false)) {
Intent dashboard_intent = new Intent(context, Dashboard.class);
- dashboard_intent.setAction(EIP.ACTION_START_EIP);
+ dashboard_intent.setAction(Constants.ACTION_START_EIP);
dashboard_intent.putExtra(Dashboard.ON_BOOT, true);
dashboard_intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(dashboard_intent);
diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java
index 8d6385e0..f22a4bfb 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Provider.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java
@@ -16,32 +16,31 @@
*/
package se.leap.bitmaskclient;
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.Locale;
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Parcel;
+import android.os.Parcelable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import android.content.Context;
-import android.app.Activity;
-import android.content.SharedPreferences;
+import java.io.File;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Locale;
/**
* @author Sean Leonard <meanderingcode@aetherislands.net>
*
*/
-public final class Provider implements Serializable {
+public final class Provider implements Parcelable {
- private static final long serialVersionUID = 6003835972151761353L;
-
- private static Provider instance = null;
-
- // We'll access our preferences here
- private static SharedPreferences preferences = null;
- // Represents our Provider's provider.json
- private static JSONObject definition = null;
+ private JSONObject definition; // Represents our Provider's provider.json
+ private URL main_url;
final public static String
API_URL = "api_uri",
@@ -69,71 +68,64 @@ public final class Provider implements Serializable {
private static final String API_TERM_DEFAULT_LANGUAGE = "default_language";
protected static final String[] API_EIP_TYPES = {"openvpn"};
- private static final String PREFS_EIP_NAME = null;
+ public Provider(URL main_url) {
+ this.main_url = main_url;
+ }
+ public Provider(File provider_file) {
-
- // What, no individual fields?! We're going to gamble on org.json.JSONObject and JSONArray
- // Supporting multiple API versions will probably break this paradigm,
- // Forcing me to write a real constructor and rewrite getters/setters
- // Also will refactor if i'm instantiating the same local variables all the time
-
- /**
- *
- */
- private Provider() {}
-
- protected static Provider getInstance(){
- if(instance==null){
- instance = new Provider();
- }
- return instance;
- }
+ }
+ public static final Parcelable.Creator<Provider> CREATOR
+ = new Parcelable.Creator<Provider>() {
+ public Provider createFromParcel(Parcel in) {
+ return new Provider(in);
+ }
- protected void init(Activity activity) {
-
- // Load our preferences from SharedPreferences
- // If there's nothing there, we will end up returning a rather empty object
- // to whoever called getInstance() and they can run the First Run Wizard
- //preferences = context.getgetPreferences(0); // 0 == MODE_PRIVATE, but we don't extend Android's classes...
-
- // Load SharedPreferences
- preferences = activity.getSharedPreferences(Dashboard.SHARED_PREFERENCES,Context.MODE_PRIVATE);
- // Inflate our provider.json data
- try {
- definition = new JSONObject( preferences.getString(Provider.KEY, "") );
- } catch (JSONException e) {
- // TODO: handle exception
-
- // FIXME!! We want "real" data!!
- }
- }
+ public Provider[] newArray(int size) {
+ return new Provider[size];
+ }
+ };
+
+ private Provider(Parcel in) {
+ try {
+ main_url = new URL(in.readString());
+ String definition_string = in.readString();
+ if(definition_string != null)
+ definition = new JSONObject((definition_string));
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ protected void define(JSONObject provider_json) {
+ definition = provider_json;
+ }
+
+ protected JSONObject definition() { return definition; }
protected String getDomain(){
- String domain = "Null";
- try {
- domain = definition.getString(API_TERM_DOMAIN);
- } catch (JSONException e) {
- domain = "Null";
- e.printStackTrace();
- }
- return domain;
+ return main_url.getHost();
}
+
+ protected URL mainUrl() {
+ return main_url;
+ }
protected String getName(){
// Should we pass the locale in, or query the system here?
String lang = Locale.getDefault().getLanguage();
- String name = "Null"; // Should it actually /be/ null, for error conditions?
+ String name = "";
try {
- name = definition.getJSONObject(API_TERM_NAME).getString(lang);
+ if(definition != null)
+ name = definition.getJSONObject(API_TERM_NAME).getString(lang);
+ else throw new JSONException("Provider not defined");
} catch (JSONException e) {
- // TODO: Nesting try/catch blocks? Crazy
- // Maybe you should actually handle exception?
- try {
- name = definition.getJSONObject(API_TERM_NAME).getString( definition.getString(API_TERM_DEFAULT_LANGUAGE) );
- } catch (JSONException e2) {
- // TODO: Will you handle the exception already?
- }
+ if(main_url != null) {
+ String host = main_url.getHost();
+ name = host.substring(0, host.indexOf("."));
+ }
}
return name;
@@ -157,58 +149,60 @@ public final class Provider implements Serializable {
}
protected boolean hasEIP() {
- JSONArray services = null;
try {
- services = definition.getJSONArray(API_TERM_SERVICES); // returns ["openvpn"]
+ JSONArray services = definition.getJSONArray(API_TERM_SERVICES); // returns ["openvpn"]
+ for (int i=0;i<API_EIP_TYPES.length+1;i++){
+ try {
+ // Walk the EIP types array looking for matches in provider's service definitions
+ if ( Arrays.asList(API_EIP_TYPES).contains( services.getString(i) ) )
+ return true;
+ } catch (NullPointerException e){
+ e.printStackTrace();
+ return false;
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ }
+ }
} catch (Exception e) {
// TODO: handle exception
}
- for (int i=0;i<API_EIP_TYPES.length+1;i++){
- try {
- // Walk the EIP types array looking for matches in provider's service definitions
- if ( Arrays.asList(API_EIP_TYPES).contains( services.getString(i) ) )
- return true;
- } catch (NullPointerException e){
- e.printStackTrace();
- return false;
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return false;
- }
- }
return false;
}
-
- protected String getEIPType() {
- // FIXME!!!!! We won't always be providing /only/ OpenVPN, will we?
- // This will have to hook into some saved choice of EIP transport
- if ( instance.hasEIP() )
- return "OpenVPN";
- else
- return null;
- }
-
- protected JSONObject getEIP() {
- // FIXME!!!!! We won't always be providing /only/ OpenVPN, will we?
- // This will have to hook into some saved choice of EIP transport, cluster, gateway
- // with possible "choose at random" preference
- if ( instance.hasEIP() ){
- // TODO Might need an EIP class, but we've only got OpenVPN type right now,
- // and only one gateway for our only provider...
- // TODO We'll try to load from preferences, have to call ProviderAPI if we've got nothin...
- JSONObject eipObject = null;
- try {
- eipObject = new JSONObject( preferences.getString(PREFS_EIP_NAME, "") );
- } catch (JSONException e) {
- // TODO ConfigHelper.rescueJSON()
- // Still nothing?
- // TODO ProviderAPI.getEIP()
- e.printStackTrace();
- }
-
- return eipObject;
- } else
- return null;
- }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeString(main_url.toString());
+ if(definition != null)
+ parcel.writeString(definition.toString());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(o instanceof Provider) {
+ Provider p = (Provider) o;
+ return p.mainUrl().equals(mainUrl());
+ } else return false;
+ }
+
+ public JSONObject toJson() {
+ JSONObject json = new JSONObject();
+ try {
+ json.put(Provider.MAIN_URL, main_url);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ @Override
+ public int hashCode() {
+ return main_url.hashCode();
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java
index 7b256124..7e4e95d3 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
- package se.leap.bitmaskclient;
+package se.leap.bitmaskclient;
import android.os.Bundle;
import android.os.Handler;
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListAdapter.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListAdapter.java
index 43bba085..c63e2edb 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderListAdapter.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListAdapter.java
@@ -1,7 +1,5 @@
package se.leap.bitmaskclient;
-import java.util.List;
-
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
@@ -9,7 +7,15 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TwoLineListItem;
-public class ProviderListAdapter<T> extends ArrayAdapter<T> {
+import com.pedrogomez.renderers.AdapteeCollection;
+import com.pedrogomez.renderers.RendererAdapter;
+import com.pedrogomez.renderers.RendererBuilder;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+public class ProviderListAdapter extends RendererAdapter<Provider> {
private static boolean[] hidden = null;
public void hide(int position) {
@@ -23,10 +29,23 @@ public class ProviderListAdapter<T> extends ArrayAdapter<T> {
notifyDataSetChanged();
notifyDataSetInvalidated();
}
+
+ public void showAllProviders() {
+ for(int i = 0; i < hidden.length; i++)
+ hidden[i] = false;
+ notifyDataSetChanged();
+ notifyDataSetInvalidated();
+ }
- public void unHideAll() {
- for (int provider_index = 0; provider_index < hidden.length; provider_index++)
- hidden[provider_index] = false;
+ public void hideAllBut(int position) {
+ for (int i = 0; i < hidden.length; i++) {
+ if (i != position)
+ hidden[i] = true;
+ else
+ hidden[i] = false;
+ }
+ notifyDataSetChanged();
+ notifyDataSetInvalidated();
}
private int getRealPosition(int position) {
@@ -60,55 +79,52 @@ public class ProviderListAdapter<T> extends ArrayAdapter<T> {
return (hidden.length - getHiddenCount());
}
- public ProviderListAdapter(Context mContext, int layout, List<T> objects) {
- super(mContext, layout, objects);
- if(hidden == null) {
- hidden = new boolean[objects.size()];
- for (int i = 0; i < objects.size(); i++)
- hidden[i] = false;
- }
- }
-
- public ProviderListAdapter(Context mContext, int layout, List<T> objects, boolean show_all_providers) {
- super(mContext, layout, objects);
- if(show_all_providers) {
- hidden = new boolean[objects.size()];
- for (int i = 0; i < objects.size(); i++)
- hidden[i] = false;
- }
- }
+ public ProviderListAdapter(LayoutInflater layoutInflater, RendererBuilder rendererBuilder,
+ AdapteeCollection<Provider> collection) {
+ super(layoutInflater, rendererBuilder, collection);
+ hidden = new boolean[collection.size()];
+ for (int i = 0; i < collection.size(); i++)
+ hidden[i] = false;
+ }
@Override
- public void add(T item) {
+ public void add(Provider item) {
super.add(item);
- boolean[] new_hidden = new boolean[hidden.length+1];
- System.arraycopy(hidden, 0, new_hidden, 0, hidden.length);
- new_hidden[hidden.length] = false;
- hidden = new_hidden;
+ if(getCollection().size() > hidden.length) {
+ boolean[] new_hidden = new boolean[hidden.length + 1];
+ System.arraycopy(hidden, 0, new_hidden, 0, hidden.length);
+ new_hidden[hidden.length] = false;
+ hidden = new_hidden;
+ }
}
@Override
- public void remove(T item) {
+ public void remove(Provider item) {
super.remove(item);
boolean[] new_hidden = new boolean[hidden.length-1];
System.arraycopy(hidden, 0, new_hidden, 0, hidden.length-1);
hidden = new_hidden;
}
- @Override
- public View getView(int index, View convertView, ViewGroup parent) {
- TwoLineListItem row;
- int position = getRealPosition(index);
- if (convertView == null) {
- LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- row = (TwoLineListItem)inflater.inflate(R.layout.provider_list_item, null);
- } else {
- row = (TwoLineListItem)convertView;
- }
- ProviderListContent.ProviderItem data = ProviderListContent.ITEMS.get(position);
- row.getText1().setText(data.domain());
- row.getText2().setText(data.name());
+ protected int indexOf(Provider item) {
+ int index = 0;
+ ProviderManager provider_manager = (ProviderManager) getCollection();
+ Set<Provider> providers = provider_manager.providers();
+ for (Provider provider : providers) {
+ if (provider.equals(item)) {
+ break;
+ } else index++;
+ }
+ return index;
+ }
- return row;
- }
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return super.getView(getRealPosition(position), convertView, parent);
+ }
+
+ public void saveProviders() {
+ ProviderManager provider_manager = (ProviderManager) getCollection();
+ provider_manager.saveCustomProvidersToFile();
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListFragment.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListFragment.java
deleted file mode 100644
index db414d87..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderListFragment.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/**
- * 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 <http://www.gnu.org/licenses/>.
- */
- package se.leap.bitmaskclient;
-
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.ProviderListContent.ProviderItem;
-import android.app.Activity;
-import android.app.ListFragment;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ListView;
-
-/**
- * A list fragment representing a list of Providers. This fragment
- * also supports tablet devices by allowing list items to be given an
- * 'activated' state upon selection. This helps indicate which item is
- * currently being viewed in a {@link DashboardFragment}.
- * <p>
- * Activities containing this fragment MUST implement the {@link Callbacks}
- * interface.
- */
-public class ProviderListFragment extends ListFragment {
-
- public static String TAG = "provider_list_fragment";
- public static String SHOW_ALL_PROVIDERS = "show_all_providers";
- public static String TOP_PADDING = "top padding from providerlistfragment";
- private ProviderListAdapter<ProviderItem> content_adapter;
-
- /**
- * The serialization (saved instance state) Bundle key representing the
- * activated item position. Only used on tablets.
- */
- private static final String STATE_ACTIVATED_POSITION = "activated_position";
-
- /**
- * The fragment's current callback object, which is notified of list item
- * clicks.
- */
- private Callbacks mCallbacks = sDummyCallbacks;
-
- /**
- * The current activated item position. Only used on tablets.
- */
- private int mActivatedPosition = ListView.INVALID_POSITION;
-
- /**
- * A callback interface that all activities containing this fragment must
- * implement. This mechanism allows activities to be notified of item
- * selections.
- */
- public interface Callbacks {
- /**
- * Callback for when an item has been selected.
- */
- public void onItemSelected(String id);
- }
-
- /**
- * A dummy implementation of the {@link Callbacks} interface that does
- * nothing. Used only when this fragment is not attached to an activity.
- */
- private static Callbacks sDummyCallbacks = new Callbacks() {
- @Override
- public void onItemSelected(String id) {
- }
- };
-
- /**
- * Mandatory empty constructor for the fragment manager to instantiate the
- * fragment (e.g. upon screen orientation changes).
- */
- public ProviderListFragment() {
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if(getArguments().containsKey(SHOW_ALL_PROVIDERS))
- content_adapter = new ProviderListAdapter<ProviderListContent.ProviderItem>(
- getActivity(),
- R.layout.provider_list_item,
- ProviderListContent.ITEMS, getArguments().getBoolean(SHOW_ALL_PROVIDERS));
- else
- content_adapter = new ProviderListAdapter<ProviderListContent.ProviderItem>(
- getActivity(),
- R.layout.provider_list_item,
- ProviderListContent.ITEMS);
-
-
- setListAdapter(content_adapter);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- return inflater.inflate(R.layout.provider_list_fragment, container, false);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- // Restore the previously serialized activated item position.
- if (savedInstanceState != null
- && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
- setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
- }
- if(getArguments() != null && getArguments().containsKey(TOP_PADDING)) {
- int topPadding = getArguments().getInt(TOP_PADDING);
- View current_view = getView();
- getView().setPadding(current_view.getPaddingLeft(), topPadding, current_view.getPaddingRight(), current_view.getPaddingBottom());
- }
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- // Activities containing this fragment must implement its callbacks.
- if (!(activity instanceof Callbacks)) {
- throw new IllegalStateException("Activity must implement fragment's callbacks.");
- }
-
- mCallbacks = (Callbacks) activity;
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
-
- // Reset the active callbacks interface to the dummy implementation.
- mCallbacks = sDummyCallbacks;
- }
-
- @Override
- public void onListItemClick(ListView listView, View view, int position, long id) {
- super.onListItemClick(listView, view, position, id);
-
- // Notify the active callbacks interface (the activity, if the
- // fragment is attached to one) that an item has been selected.
- mCallbacks.onItemSelected(ProviderListContent.ITEMS.get(position).name());
-
- for(int item_position = 0; item_position < listView.getCount(); item_position++) {
- if(item_position != position)
- content_adapter.hide(item_position);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if (mActivatedPosition != ListView.INVALID_POSITION) {
- // Serialize and persist the activated item position.
- outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
- }
- }
-
- public void notifyAdapter() {
- content_adapter.notifyDataSetChanged();
- }
- /**
- * Turns on activate-on-click mode. When this mode is on, list items will be
- * given the 'activated' state when touched.
- */
- public void setActivateOnItemClick(boolean activateOnItemClick) {
- // When setting CHOICE_MODE_SINGLE, ListView will automatically
- // give items the 'activated' state when touched.
- getListView().setChoiceMode(activateOnItemClick
- ? ListView.CHOICE_MODE_SINGLE
- : ListView.CHOICE_MODE_NONE);
- }
-
- private void setActivatedPosition(int position) {
- if (position == ListView.INVALID_POSITION) {
- getListView().setItemChecked(mActivatedPosition, false);
- } else {
- getListView().setItemChecked(position, true);
- }
-
- mActivatedPosition = position;
- }
-
- public void removeLastItem() {
- unhideAll();
- content_adapter.remove(content_adapter.getItem(content_adapter.getCount()-1));
- content_adapter.notifyDataSetChanged();
- }
-
- public void addItem(ProviderItem provider) {
- content_adapter.add(provider);
- content_adapter.notifyDataSetChanged();
- }
-
- public void hideAllBut(int position) {
- int real_count = content_adapter.getCount();
- for(int i = 0; i < real_count;)
- if(i != position) {
- content_adapter.hide(i);
- position--;
- real_count--;
- } else {
- i++;
- }
- }
-
- public void unhideAll() {
- if(content_adapter != null) {
- content_adapter.unHideAll();
- content_adapter.notifyDataSetChanged();
- }
- }
-
- /**
- * @return a new instance of this ListFragment.
- */
- public static ProviderListFragment newInstance() {
- return new ProviderListFragment();
- }
-}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java
new file mode 100644
index 00000000..911144f7
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java
@@ -0,0 +1,178 @@
+package se.leap.bitmaskclient;
+
+import android.content.res.AssetManager;
+
+import com.pedrogomez.renderers.AdapteeCollection;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Created by parmegv on 4/12/14.
+ */
+public class ProviderManager implements AdapteeCollection<Provider> {
+
+ private AssetManager assets_manager;
+ private File external_files_dir;
+ private Set<Provider> default_providers;
+ private Set<Provider> custom_providers;
+
+ private static ProviderManager instance;
+
+ final protected static String URLS = "urls";
+
+ public static ProviderManager getInstance(AssetManager assets_manager, File external_files_dir) {
+ if(instance == null)
+ instance = new ProviderManager(assets_manager);
+
+ instance.addCustomProviders(external_files_dir);
+ return instance;
+ }
+
+ public ProviderManager(AssetManager assets_manager) {
+ this.assets_manager = assets_manager;
+ addDefaultProviders(assets_manager);
+ }
+
+ private void addDefaultProviders(AssetManager assets_manager) {
+ try {
+ default_providers = providersFromAssets(URLS, assets_manager.list(URLS));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private Set<Provider> providersFromAssets(String directory, String[] relative_file_paths) {
+ Set<Provider> providers = new HashSet<Provider>();
+ try {
+ for(String file : relative_file_paths) {
+ String main_url = extractMainUrlFromInputStream(assets_manager.open(directory + "/" + file));
+ providers.add(new Provider(new URL(main_url)));
+ }
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return providers;
+ }
+
+
+ private void addCustomProviders(File external_files_dir) {
+ this.external_files_dir = external_files_dir;
+ custom_providers = external_files_dir.isDirectory() ?
+ providersFromFiles(external_files_dir.list()) :
+ new HashSet<Provider>();
+ }
+
+ private Set<Provider> providersFromFiles(String[] files) {
+ Set<Provider> providers = new HashSet<Provider>();
+ try {
+ for(String file : files) {
+ String main_url = extractMainUrlFromInputStream(new FileInputStream(external_files_dir.getAbsolutePath() + "/" + file));
+ providers.add(new Provider(new URL(main_url)));
+ }
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ return providers;
+ }
+
+ private String extractMainUrlFromInputStream(InputStream input_stream_file_contents) {
+ String main_url = "";
+ byte[] bytes = new byte[0];
+ try {
+ bytes = new byte[input_stream_file_contents.available()];
+ if(input_stream_file_contents.read(bytes) > 0) {
+ JSONObject file_contents = new JSONObject(new String(bytes));
+ main_url = file_contents.getString(Provider.MAIN_URL);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ return main_url;
+ }
+
+ public Set<Provider> providers() {
+ Set<Provider> all_providers = new HashSet<Provider>();
+ all_providers.addAll(default_providers);
+ all_providers.addAll(custom_providers);
+ return all_providers;
+ }
+
+ @Override
+ public int size() {
+ return providers().size();
+ }
+
+ @Override
+ public Provider get(int index) {
+ Iterator<Provider> iterator = providers().iterator();
+ while(iterator.hasNext() && index > 0) {
+ iterator.next();
+ index--;
+ }
+ return iterator.next();
+ }
+
+ @Override
+ public void add(Provider element) {
+ custom_providers.add(element);
+ }
+
+ @Override
+ public void remove(Provider element) {
+ custom_providers.remove(element);
+ }
+
+ @Override
+ public void addAll(Collection<Provider> elements) {
+ custom_providers.addAll(elements);
+ }
+
+ @Override
+ public void removeAll(Collection<Provider> elements) {
+ custom_providers.removeAll(elements);
+ default_providers.removeAll(elements);
+ }
+
+ @Override
+ public void clear() {
+ default_providers.clear();
+ custom_providers.clear();
+ }
+
+ protected void saveCustomProvidersToFile() {
+ try {
+ for (Provider provider : custom_providers) {
+ File provider_file = new File(external_files_dir, provider.getName() + ".json");
+ if(!provider_file.exists()) {
+ FileWriter writer = new FileWriter(provider_file);
+ writer.write(provider.toJson().toString());
+ writer.close();
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderRenderer.java b/app/src/main/java/se/leap/bitmaskclient/ProviderRenderer.java
new file mode 100644
index 00000000..6e194e84
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderRenderer.java
@@ -0,0 +1,57 @@
+package se.leap.bitmaskclient;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.pedrogomez.renderers.Renderer;
+
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+import butterknife.OnItemClick;
+import butterknife.OnItemSelected;
+
+/**
+ * Created by parmegv on 4/12/14.
+ */
+public class ProviderRenderer extends Renderer<Provider> {
+ private final Context context;
+
+ @InjectView(R.id.provider_name)
+ TextView name;
+ @InjectView(R.id.provider_domain)
+ TextView domain;
+
+ public ProviderRenderer(Context context) {
+ this.context = context;
+ }
+
+ @Override
+ protected View inflate(LayoutInflater inflater, ViewGroup parent) {
+ View view = inflater.inflate(R.layout.provider_list_item, parent, false);
+ ButterKnife.inject(this, view);
+ return view;
+ }
+
+ @Override
+ protected void setUpView(View rootView) {
+ /*
+ * Empty implementation substituted with the usage of ButterKnife library by Jake Wharton.
+ */
+ }
+
+ @Override
+ protected void hookListeners(View rootView) {
+ //Empty
+ }
+
+ @Override
+ public void render() {
+ Provider provider = getContent();
+ name.setText(provider.getName());
+ domain.setText(provider.getDomain());
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderRendererBuilder.java b/app/src/main/java/se/leap/bitmaskclient/ProviderRendererBuilder.java
new file mode 100644
index 00000000..7366e68e
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderRendererBuilder.java
@@ -0,0 +1,25 @@
+package se.leap.bitmaskclient;
+
+import android.content.Context;
+
+import com.pedrogomez.renderers.Renderer;
+import com.pedrogomez.renderers.RendererBuilder;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Created by parmegv on 4/12/14.
+ */
+ public class ProviderRendererBuilder extends RendererBuilder<Provider> {
+ public ProviderRendererBuilder(Collection<Renderer<Provider>> prototypes) {
+ super(prototypes);
+ }
+ @Override
+ protected Class getPrototypeClass(Provider content) {
+ return ProviderRenderer.class;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/LogInDialog.java b/app/src/main/java/se/leap/bitmaskclient/SessionDialog.java
index 45d3a373..60382cf0 100644
--- a/app/src/main/java/se/leap/bitmaskclient/LogInDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/SessionDialog.java
@@ -16,22 +16,19 @@
*/
package se.leap.bitmaskclient;
-import se.leap.bitmaskclient.R;
-import android.R.color;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
-import android.content.res.ColorStateList;
import android.os.Bundle;
-import android.provider.CalendarContract.Colors;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.BounceInterpolator;
import android.widget.EditText;
import android.widget.TextView;
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+
/**
* Implements the log in dialog, currently without progress dialog.
*
@@ -42,58 +39,62 @@ import android.widget.TextView;
* @author parmegv
*
*/
-public class LogInDialog extends DialogFragment {
+public class SessionDialog extends DialogFragment{
- final public static String TAG = "logInDialog";
- final public static String VERB = "log in";
- final public static String USERNAME = "username";
- final public static String PASSWORD = "password";
- final public static String USERNAME_MISSING = "username missing";
- final public static String PASSWORD_INVALID_LENGTH = "password_invalid_length";
+ final public static String TAG = SessionDialog.class.getSimpleName();
+
+ final public static String USERNAME = "username";
+ final public static String PASSWORD = "password";
+ final public static String USERNAME_MISSING = "username missing";
+ final public static String PASSWORD_INVALID_LENGTH = "password_invalid_length";
+
+ @InjectView(R.id.user_message)
+ TextView user_message;
+ @InjectView(R.id.username_entered)
+ EditText username_field;
+ @InjectView(R.id.password_entered)
+ EditText password_field;
+
+ private static SessionDialog dialog;
private static boolean is_eip_pending = false;
public AlertDialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
- View log_in_dialog_view = inflater.inflate(R.layout.log_in_dialog, null);
+ View view = inflater.inflate(R.layout.session_dialog, null);
+ ButterKnife.inject(this, view);
- final TextView user_message = (TextView)log_in_dialog_view.findViewById(R.id.user_message);
- if(getArguments() != null && getArguments().containsKey(getResources().getString(R.string.user_message))) {
- user_message.setText(getArguments().getString(getResources().getString(R.string.user_message)));
- } else {
- user_message.setVisibility(View.GONE);
- }
-
- final EditText username_field = (EditText)log_in_dialog_view.findViewById(R.id.username_entered);
- if(getArguments() != null && getArguments().containsKey(USERNAME)) {
- String username = getArguments().getString(USERNAME);
- username_field.setText(username);
- }
- if (getArguments() != null && getArguments().containsKey(USERNAME_MISSING)) {
- username_field.setError(getResources().getString(R.string.username_ask));
- }
-
- final EditText password_field = (EditText)log_in_dialog_view.findViewById(R.id.password_entered);
if(!username_field.getText().toString().isEmpty() && password_field.isFocusable()) {
password_field.requestFocus();
}
- if (getArguments() != null && getArguments().containsKey(PASSWORD_INVALID_LENGTH)) {
- password_field.setError(getResources().getString(R.string.error_not_valid_password_user_message));
- }
- if(getArguments() != null && getArguments().getBoolean(EipServiceFragment.IS_EIP_PENDING, false)) {
- is_eip_pending = true;
+
+ Bundle arguments = getArguments();
+ if (arguments != null) {
+ is_eip_pending = arguments.getBoolean(EipServiceFragment.IS_PENDING, false);
+ if (arguments.containsKey(PASSWORD_INVALID_LENGTH))
+ password_field.setError(getString(R.string.error_not_valid_password_user_message));
+ if (arguments.containsKey(USERNAME)) {
+ String username = arguments.getString(USERNAME);
+ username_field.setText(username);
}
-
+ if (arguments.containsKey(USERNAME_MISSING)) {
+ username_field.setError(getString(R.string.username_ask));
+ }
+ if(arguments.containsKey(getString(R.string.user_message)))
+ user_message.setText(arguments.getString(getString(R.string.user_message)));
+ else
+ user_message.setVisibility(View.GONE);
+ }
- builder.setView(log_in_dialog_view)
+ builder.setView(view)
.setPositiveButton(R.string.login_button, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
String username = username_field.getText().toString();
String password = password_field.getText().toString();
dialog.dismiss();
- interface_with_Dashboard.authenticate(username, password);
+ interface_with_Dashboard.logIn(username, password);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@@ -112,35 +113,37 @@ public class LogInDialog extends DialogFragment {
return builder.create();
}
+
/**
- * Interface used to communicate LogInDialog with Dashboard.
+ * Interface used to communicate SessionDialog with Dashboard.
*
* @author parmegv
*
*/
- public interface LogInDialogInterface {
- public void authenticate(String username, String password);
- public void cancelAuthedEipOn();
+ public interface SessionDialogInterface {
+ public void logIn(String username, String password);
public void signUp(String username, String password);
public void cancelLoginOrSignup();
}
- LogInDialogInterface interface_with_Dashboard;
+ SessionDialogInterface interface_with_Dashboard;
/**
* @return a new instance of this DialogFragment.
*/
public static DialogFragment newInstance() {
- LogInDialog dialog_fragment = new LogInDialog();
- return dialog_fragment;
+ if(dialog == null)
+ dialog = new SessionDialog();
+
+ return dialog;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
- interface_with_Dashboard = (LogInDialogInterface) activity;
+ interface_with_Dashboard = (SessionDialogInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement LogInDialogListener");
@@ -149,8 +152,8 @@ public class LogInDialog extends DialogFragment {
@Override
public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
if(is_eip_pending)
- interface_with_Dashboard.cancelAuthedEipOn();
- super.onCancel(dialog);
+ interface_with_Dashboard.cancelLoginOrSignup();
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/SignUpDialog.java b/app/src/main/java/se/leap/bitmaskclient/SignUpDialog.java
deleted file mode 100644
index 120d4eec..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/SignUpDialog.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * 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 <http://www.gnu.org/licenses/>.
- */
- package se.leap.bitmaskclient;
-
-import se.leap.bitmaskclient.R;
-import android.R.color;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.content.res.ColorStateList;
-import android.os.Bundle;
-import android.provider.CalendarContract.Colors;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.BounceInterpolator;
-import android.widget.EditText;
-import android.widget.TextView;
-
-/**
- * Implements the sign up dialog, currently without progress dialog.
- *
- * It returns to the previous fragment when finished, and sends username and password to the registration method.
- *
- * It also notifies the user if the password is not valid.
- *
- * @author parmegv
- *
- */
-public class SignUpDialog extends DialogFragment {
-
- final public static String TAG = "signUpDialog";
- final public static String VERB = "log in";
- final public static String USERNAME = "username";
- final public static String PASSWORD = "password";
- final public static String USERNAME_MISSING = "username missing";
- final public static String PASSWORD_INVALID_LENGTH = "password_invalid_length";
-
- private static boolean is_eip_pending = false;
-
- public AlertDialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- LayoutInflater inflater = getActivity().getLayoutInflater();
- View log_in_dialog_view = inflater.inflate(R.layout.log_in_dialog, null);
-
- final TextView user_message = (TextView)log_in_dialog_view.findViewById(R.id.user_message);
- if(getArguments() != null && getArguments().containsKey(getResources().getString(R.string.user_message))) {
- user_message.setText(getArguments().getString(getResources().getString(R.string.user_message)));
- } else {
- user_message.setVisibility(View.GONE);
- }
-
- final EditText username_field = (EditText)log_in_dialog_view.findViewById(R.id.username_entered);
- if(getArguments() != null && getArguments().containsKey(USERNAME)) {
- String username = getArguments().getString(USERNAME);
- username_field.setText(username);
- }
- if (getArguments() != null && getArguments().containsKey(USERNAME_MISSING)) {
- username_field.setError(getResources().getString(R.string.username_ask));
- }
-
- final EditText password_field = (EditText)log_in_dialog_view.findViewById(R.id.password_entered);
- if(!username_field.getText().toString().isEmpty() && password_field.isFocusable()) {
- password_field.requestFocus();
- }
- if (getArguments() != null && getArguments().containsKey(PASSWORD_INVALID_LENGTH)) {
- password_field.setError(getResources().getString(R.string.error_not_valid_password_user_message));
- }
- if(getArguments() != null && getArguments().getBoolean(EipServiceFragment.IS_EIP_PENDING, false)) {
- is_eip_pending = true;
- }
-
-
- builder.setView(log_in_dialog_view)
- .setPositiveButton(R.string.signup_button, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- String username = username_field.getText().toString();
- String password = password_field.getText().toString();
- dialog.dismiss();
- interface_with_Dashboard.signUp(username, password);
- }
- })
- .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- interface_with_Dashboard.cancelLoginOrSignup();
- }
- });
-
- return builder.create();
- }
-
- /**
- * Interface used to communicate SignUpDialog with Dashboard.
- *
- * @author parmegv
- *
- */
- public interface SignUpDialogInterface {
- public void signUp(String username, String password);
- public void cancelAuthedEipOn();
- public void cancelLoginOrSignup();
- }
-
- SignUpDialogInterface interface_with_Dashboard;
-
- /**
- * @return a new instance of this DialogFragment.
- */
- public static DialogFragment newInstance() {
- SignUpDialog dialog_fragment = new SignUpDialog();
- return dialog_fragment;
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- try {
- interface_with_Dashboard = (SignUpDialogInterface) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement SignUpDialogListener");
- }
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- if(is_eip_pending)
- interface_with_Dashboard.cancelAuthedEipOn();
- super.onCancel(dialog);
- }
-}
diff --git a/app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java b/app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java
deleted file mode 100644
index b7289c23..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package se.leap.bitmaskclient;
-
-import android.content.Intent;
-import android.net.VpnService;
-import android.util.Log;
-
-public class VoidVpnService extends VpnService {
-
- static final String START_BLOCKING_VPN_PROFILE = "se.leap.bitmaskclient.START_BLOCKING_VPN_PROFILE";
- static final String TAG = VoidVpnService.class.getSimpleName();
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- String action = intent.getAction();
- if (action == START_BLOCKING_VPN_PROFILE) {
- new Thread(new Runnable() {
- public void run() {
- blockConnections();
- }
- }).run();
- }
- return 0;
- }
-
- public void blockConnections() {
- Builder builder = new Builder();
- builder.setSession("Blocking until running");
- builder.addAddress("10.42.0.8",16);
- builder.addRoute("0.0.0.0", 1);
- builder.addRoute("128.0.0.0", 1);
- builder.addRoute("192.168.1.0", 24);
- builder.addDnsServer("10.42.0.1");
- builder.establish();
- android.util.Log.d(TAG, "VoidVpnService set up");
- try {
- new java.net.Socket("sdf.org", 80);
- Log.d(TAG, "VoidVpnService doesn's stop traffic");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java b/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java
new file mode 100644
index 00000000..12c2e015
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java
@@ -0,0 +1,47 @@
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+/**
+ *
+ * Constants for intent passing, shared preferences
+ *
+ * @author Parménides GV <parmegv@sdf.org>
+ *
+ */
+public interface Constants {
+
+ public final static String TAG = Constants.class.getSimpleName();
+
+ public final static String AUTHED_EIP = TAG + ".AUTHED_EIP";
+ public final static String ACTION_CHECK_CERT_VALIDITY = TAG + ".CHECK_CERT_VALIDITY";
+ public final static String ACTION_START_EIP = TAG + ".START_EIP";
+ public final static String ACTION_STOP_EIP = TAG + ".STOP_EIP";
+ public final static String ACTION_UPDATE_EIP_SERVICE = TAG + ".UPDATE_EIP_SERVICE";
+ public final static String ACTION_IS_EIP_RUNNING = TAG + ".IS_RUNNING";
+ public final static String EIP_NOTIFICATION = TAG + ".EIP_NOTIFICATION";
+ public final static String ALLOWED_ANON = "allow_anonymous";
+ public final static String ALLOWED_REGISTERED = "allow_registration";
+ public final static String CERTIFICATE = "cert";
+ public final static String PRIVATE_KEY = TAG + ".PRIVATE_KEY";
+ public final static String KEY = TAG + ".KEY";
+ public final static String RECEIVER_TAG = TAG + ".RECEIVER_TAG";
+ public final static String REQUEST_TAG = TAG + ".REQUEST_TAG";
+ public final static String START_BLOCKING_VPN_PROFILE = TAG + ".START_BLOCKING_VPN_PROFILE";
+ public final static String PROVIDER_CONFIGURED = TAG + ".PROVIDER_CONFIGURED";
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
new file mode 100644
index 00000000..0713e521
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -0,0 +1,251 @@
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.app.Activity;
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import de.blinkt.openvpn.LaunchVPN;
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.ProfileManager;
+import se.leap.bitmaskclient.Dashboard;
+import se.leap.bitmaskclient.EipServiceFragment;
+
+import static se.leap.bitmaskclient.eip.Constants.ACTION_CHECK_CERT_VALIDITY;
+import static se.leap.bitmaskclient.eip.Constants.ACTION_IS_EIP_RUNNING;
+import static se.leap.bitmaskclient.eip.Constants.ACTION_START_EIP;
+import static se.leap.bitmaskclient.eip.Constants.ACTION_STOP_EIP;
+import static se.leap.bitmaskclient.eip.Constants.ACTION_UPDATE_EIP_SERVICE;
+import static se.leap.bitmaskclient.eip.Constants.CERTIFICATE;
+import static se.leap.bitmaskclient.eip.Constants.KEY;
+import static se.leap.bitmaskclient.eip.Constants.RECEIVER_TAG;
+import static se.leap.bitmaskclient.eip.Constants.REQUEST_TAG;
+
+/**
+ * EIP is the abstract base class for interacting with and managing the Encrypted
+ * Internet Proxy connection. Connections are started, stopped, and queried through
+ * this IntentService.
+ * Contains logic for parsing eip-service.json from the provider, configuring and selecting
+ * gateways, and controlling {@link de.blinkt.openvpn.core.OpenVPNService} connections.
+ *
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ * @author Parménides GV <parmegv@sdf.org>
+ */
+public final class EIP extends IntentService {
+
+ public final static String TAG = EIP.class.getSimpleName();
+ public final static String SERVICE_API_PATH = "config/eip-service.json";
+
+ public static final int DISCONNECT = 15;
+
+ private static Context context;
+ private static ResultReceiver mReceiver;
+ private static SharedPreferences preferences;
+
+ private static JSONObject eip_definition;
+ private static List<Gateway> gateways = new ArrayList<Gateway>();
+ private static ProfileManager profile_manager;
+ private static Gateway gateway;
+
+ public EIP(){
+ super(TAG);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ context = getApplicationContext();
+ profile_manager = ProfileManager.getInstance(context);
+
+ preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
+ refreshEipDefinition();
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ String action = intent.getAction();
+ mReceiver = intent.getParcelableExtra(RECEIVER_TAG);
+
+ if ( action.equals(ACTION_START_EIP))
+ startEIP();
+ else if (action.equals(ACTION_STOP_EIP))
+ stopEIP();
+ else if (action.equals(ACTION_IS_EIP_RUNNING))
+ isRunning();
+ else if (action.equals(ACTION_UPDATE_EIP_SERVICE))
+ updateEIPService();
+ else if (action.equals(ACTION_CHECK_CERT_VALIDITY))
+ checkCertValidity();
+ }
+
+ /**
+ * Initiates an EIP connection by selecting a gateway and preparing and sending an
+ * Intent to {@link de.blinkt.openvpn.LaunchVPN}.
+ * It also sets up early routes.
+ */
+ private void startEIP() {
+ if(gateways.isEmpty())
+ updateEIPService();
+ earlyRoutes();
+
+ GatewaySelector gateway_selector = new GatewaySelector(gateways);
+ gateway = gateway_selector.select();
+ if(gateway != null && gateway.getProfile() != null) {
+ mReceiver = EipServiceFragment.getReceiver();
+ launchActiveGateway();
+ }
+ tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK);
+ }
+
+ /**
+ * Early routes are routes that block traffic until a new
+ * VpnService is started properly.
+ */
+ private void earlyRoutes() {
+ Intent void_vpn_launcher = new Intent(context, VoidVpnLauncher.class);
+ void_vpn_launcher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(void_vpn_launcher);
+ }
+
+ private void launchActiveGateway() {
+ Intent intent = new Intent(this,LaunchVPN.class);
+ intent.setAction(Intent.ACTION_MAIN);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(LaunchVPN.EXTRA_NAME, gateway.getProfile().getName());
+ intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
+ startActivity(intent);
+ }
+
+ private void stopEIP() {
+ EipStatus eip_status = EipStatus.getInstance();
+ Log.d(TAG, "stopEip(): eip is connected? " + eip_status.isConnected());
+ int result_code = Activity.RESULT_CANCELED;
+ if(eip_status.isConnected())
+ result_code = Activity.RESULT_OK;
+
+ tellToReceiver(ACTION_STOP_EIP, result_code);
+ }
+
+ /**
+ * Checks the last stored status notified by ics-openvpn
+ * Sends <code>Activity.RESULT_CANCELED</code> to the ResultReceiver that made the
+ * request if it's not connected, <code>Activity.RESULT_OK</code> otherwise.
+ */
+ private void isRunning() {
+ EipStatus eip_status = EipStatus.getInstance();
+ int resultCode = (eip_status.isConnected()) ?
+ Activity.RESULT_OK :
+ Activity.RESULT_CANCELED;
+ tellToReceiver(ACTION_IS_EIP_RUNNING, resultCode);
+ }
+
+ /**
+ * Loads eip-service.json from SharedPreferences, delete previous vpn profiles and add new gateways.
+ * TODO Implement API call to refresh eip-service.json from the provider
+ */
+ private void updateEIPService() {
+ refreshEipDefinition();
+ deleteAllVpnProfiles();
+ updateGateways();
+ tellToReceiver(ACTION_UPDATE_EIP_SERVICE, Activity.RESULT_OK);
+ }
+
+ private void refreshEipDefinition() {
+ try {
+ String eip_definition_string = preferences.getString(KEY, "");
+ if(!eip_definition_string.isEmpty()) {
+ eip_definition = new JSONObject(eip_definition_string);
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void deleteAllVpnProfiles() {
+ Collection<VpnProfile> profiles = profile_manager.getProfiles();
+ profiles.removeAll(profiles);
+ }
+
+ /**
+ * Walk the list of gateways defined in eip-service.json and parse them into
+ * Gateway objects.
+ * TODO Store the Gateways (as Serializable) in SharedPreferences
+ */
+ private void updateGateways(){
+ try {
+ if(eip_definition != null) {
+ JSONArray gatewaysDefined = eip_definition.getJSONArray("gateways");
+ for (int i = 0; i < gatewaysDefined.length(); i++) {
+ JSONObject gw = gatewaysDefined.getJSONObject(i);
+ if (isOpenVpnGateway(gw)) {
+ addGateway(new Gateway(eip_definition, context, gw));
+ }
+ }
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private boolean isOpenVpnGateway(JSONObject gateway) {
+ try {
+ String transport = gateway.getJSONObject("capabilities").getJSONArray("transport").toString();
+ return transport.contains("openvpn");
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+
+ private void addGateway(Gateway gateway) {
+ profile_manager.addProfile(gateway.getProfile());
+ gateways.add(gateway);
+ }
+
+ private void checkCertValidity() {
+ VpnCertificateValidator validator = new VpnCertificateValidator();
+ int resultCode = validator.isValid(preferences.getString(CERTIFICATE, "")) ?
+ Activity.RESULT_OK :
+ Activity.RESULT_CANCELED;
+ tellToReceiver(ACTION_CHECK_CERT_VALIDITY, resultCode);
+ }
+
+ private void tellToReceiver(String action, int resultCode) {
+ if (mReceiver != null){
+ Bundle resultData = new Bundle();
+ resultData.putString(REQUEST_TAG, action);
+ mReceiver.send(resultCode, resultData);
+ }
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
new file mode 100644
index 00000000..4ac3bd6a
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
@@ -0,0 +1,138 @@
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.util.Log;
+
+import java.util.Observable;
+
+import de.blinkt.openvpn.core.VpnStatus;
+
+public class EipStatus extends Observable implements VpnStatus.StateListener {
+ public static String TAG = EipStatus.class.getSimpleName();
+ private static EipStatus current_status;
+
+ private static VpnStatus.ConnectionStatus level = VpnStatus.ConnectionStatus.LEVEL_NOTCONNECTED;
+ private static boolean wants_to_disconnect = false;
+
+ private String state, log_message;
+ private int localized_res_id;
+
+ public static EipStatus getInstance() {
+ if(current_status == null) {
+ current_status = new EipStatus();
+ VpnStatus.addStateListener(current_status);
+ }
+ return current_status;
+ }
+
+ private EipStatus() { }
+
+ @Override
+ public void updateState(final String state, final String logmessage, final int localizedResId, final VpnStatus.ConnectionStatus level) {
+ current_status = getInstance();
+ current_status.setState(state);
+ current_status.setLogMessage(logmessage);
+ current_status.setLocalizedResId(localizedResId);
+ current_status.setLevel(level);
+ current_status.setChanged();
+ if(isConnected() || isDisconnected())
+ setConnectedOrDisconnected();
+ else if(isConnecting())
+ setConnecting();
+ Log.d(TAG, "update state with level " + level);
+ current_status.notifyObservers();
+ }
+
+ public boolean wantsToDisconnect() {
+ return wants_to_disconnect;
+ }
+
+ public boolean isConnecting() {
+ return
+ !isConnected() &&
+ !isDisconnected() &&
+ !isPaused();
+ }
+
+ public boolean isConnected() {
+ return level == VpnStatus.ConnectionStatus.LEVEL_CONNECTED;
+ }
+
+ public boolean isDisconnected() {
+ return level == VpnStatus.ConnectionStatus.LEVEL_NOTCONNECTED;
+ }
+
+ public boolean isPaused() {
+ return level == VpnStatus.ConnectionStatus.LEVEL_VPNPAUSED;
+ }
+
+ public void setConnecting() {
+ wants_to_disconnect = false;
+ current_status.setChanged();
+ current_status.notifyObservers();
+ }
+
+ public void setConnectedOrDisconnected() {
+ Log.d(TAG, "setConnectedOrDisconnected()");
+ wants_to_disconnect = false;
+ current_status.setChanged();
+ current_status.notifyObservers();
+ }
+
+ public void setDisconnecting() {
+ wants_to_disconnect = false;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public String getLogMessage() {
+ return log_message;
+ }
+
+ public int getLocalizedResId() {
+ return localized_res_id;
+ }
+
+ public VpnStatus.ConnectionStatus getLevel() {
+ return level;
+ }
+
+ private void setState(String state) {
+ this.state = state;
+ }
+
+ private void setLogMessage(String log_message) {
+ this.log_message = log_message;
+ }
+
+ private void setLocalizedResId(int localized_res_id) {
+ this.localized_res_id = localized_res_id;
+ }
+
+ private void setLevel(VpnStatus.ConnectionStatus level) {
+ EipStatus.level = level;
+ }
+
+ @Override
+ public String toString() {
+ return "State: " + state + " Level: " + level.toString();
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
new file mode 100644
index 00000000..3ee9443c
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -0,0 +1,156 @@
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.Iterator;
+
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.ConfigParser;
+import de.blinkt.openvpn.core.ProfileManager;
+import se.leap.bitmaskclient.Dashboard;
+
+/**
+ * Gateway provides objects defining gateways and their metadata.
+ * Each instance contains a VpnProfile for OpenVPN specific data and member
+ * variables describing capabilities and location (name)
+ *
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ * @author Parménides GV <parmegv@sdf.org>
+ */
+public class Gateway {
+
+ private String TAG = Gateway.class.getSimpleName();
+
+ private String mName;
+ private int timezone;
+ private JSONObject general_configuration;
+ private Context context;
+ private VpnProfile mVpnProfile;
+ private JSONObject mGateway;
+
+ /**
+ * Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json
+ * and create a VpnProfile belonging to it.
+ *
+ * @param gateway The JSON OpenVPN gateway definition to parse
+ */
+ protected Gateway(JSONObject eip_definition, Context context, JSONObject gateway){
+
+ mGateway = gateway;
+
+ this.context = context;
+ general_configuration = getGeneralConfiguration(eip_definition);
+ timezone = getTimezone(eip_definition);
+ mName = locationAsName(eip_definition);
+
+ // Currently deletes VpnProfile for host, if there already is one, and builds new
+ ProfileManager vpl = ProfileManager.getInstance(context);
+ Collection<VpnProfile> profiles = vpl.getProfiles();
+ for (Iterator<VpnProfile> it = profiles.iterator(); it.hasNext(); ){
+ VpnProfile p = it.next();
+
+ if ( p.mName.equalsIgnoreCase( mName ) ) {
+ it.remove();
+ vpl.removeProfile(context, p);
+ }
+ }
+
+ mVpnProfile = createVPNProfile();
+ mVpnProfile.mName = mName;
+
+ vpl.addProfile(mVpnProfile);
+ vpl.saveProfile(context, mVpnProfile);
+ vpl.saveProfileList(context);
+ }
+
+ private JSONObject getGeneralConfiguration(JSONObject eip_definition) {
+ try {
+ return eip_definition.getJSONObject("openvpn_configuration");
+ } catch (JSONException e) {
+ return new JSONObject();
+ }
+ }
+
+ private int getTimezone(JSONObject eip_definition) {
+ JSONObject location = getLocationInfo(eip_definition);
+ return location.optInt("timezone");
+ }
+
+ private String locationAsName(JSONObject eip_definition) {
+ JSONObject location = getLocationInfo(eip_definition);
+ return location.optString("name");
+ }
+
+ private JSONObject getLocationInfo(JSONObject eip_definition) {
+ try {
+ JSONObject locations = eip_definition.getJSONObject("locations");
+
+ return locations.getJSONObject(mGateway.getString("location"));
+ } catch (JSONException e) {
+ return new JSONObject();
+ }
+ }
+
+ /**
+ * Create and attach the VpnProfile to our gateway object
+ */
+ private VpnProfile createVPNProfile(){
+ try {
+ ConfigParser cp = new ConfigParser();
+
+ SharedPreferences preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE);
+ VpnConfigGenerator vpn_configuration_generator = new VpnConfigGenerator(preferences, general_configuration, mGateway);
+ String configuration = vpn_configuration_generator.generate();
+
+ cp.parseConfig(new StringReader(configuration));
+ return cp.convertProfile();
+ } catch (ConfigParser.ConfigParseError e) {
+ // FIXME We didn't get a VpnProfile! Error handling! and log level
+ Log.v(TAG,"Error creating VPNProfile");
+ e.printStackTrace();
+ return null;
+ } catch (IOException e) {
+ // FIXME We didn't get a VpnProfile! Error handling! and log level
+ Log.v(TAG,"Error creating VPNProfile");
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public VpnProfile getProfile() {
+ return mVpnProfile;
+ }
+
+ public int getTimezone() {
+ return timezone;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
new file mode 100644
index 00000000..39ae7ca6
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
@@ -0,0 +1,46 @@
+package se.leap.bitmaskclient.eip;
+
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeMap;
+
+public class GatewaySelector {
+ List<Gateway> gateways;
+
+ public GatewaySelector(List<Gateway> gateways) {
+ this.gateways = gateways;
+ }
+
+ public Gateway select() {
+ return closestGateway();
+ }
+
+ private Gateway closestGateway() {
+ TreeMap<Integer, Set<Gateway>> offsets = calculateOffsets();
+ return offsets.isEmpty() ? null : offsets.firstEntry().getValue().iterator().next();
+ }
+
+ private TreeMap<Integer, Set<Gateway>> calculateOffsets() {
+ TreeMap<Integer, Set<Gateway>> offsets = new TreeMap<Integer, Set<Gateway>>();
+ int localOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
+ for(Gateway gateway : gateways) {
+ int dist = timezoneDistance(localOffset, gateway.getTimezone());
+ Set<Gateway> set = (offsets.get(dist) != null) ?
+ offsets.get(dist) : new HashSet<Gateway>();
+ set.add(gateway);
+ offsets.put(dist, set);
+ }
+ return offsets;
+ }
+
+ private int timezoneDistance(int local_timezone, int remote_timezone) {
+ // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
+ int dist = Math.abs(local_timezone - remote_timezone);
+ // Farther than 12 timezones and it's shorter around the "back"
+ if (dist > 12)
+ dist = 12 - (dist -12); // Well i'll be. Absolute values make equations do funny things.
+ return dist;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/VoidVpnLauncher.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
index 3b286fbf..d79d8003 100644
--- a/app/src/main/java/se/leap/bitmaskclient/VoidVpnLauncher.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
@@ -1,4 +1,4 @@
-package se.leap.bitmaskclient;
+package se.leap.bitmaskclient.eip;
import android.app.Activity;
import android.content.Intent;
@@ -8,7 +8,7 @@ import android.os.Bundle;
public class VoidVpnLauncher extends Activity {
private static final int VPN_USER_PERMISSION = 71;
-
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -28,7 +28,7 @@ public class VoidVpnLauncher extends Activity {
if(requestCode == VPN_USER_PERMISSION) {
if(resultCode == RESULT_OK) {
Intent void_vpn_service = new Intent(getApplicationContext(), VoidVpnService.class);
- void_vpn_service.setAction(VoidVpnService.START_BLOCKING_VPN_PROFILE);
+ void_vpn_service.setAction(Constants.START_BLOCKING_VPN_PROFILE);
startService(void_vpn_service);
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
new file mode 100644
index 00000000..a6f9fe76
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
@@ -0,0 +1,33 @@
+package se.leap.bitmaskclient.eip;
+
+import android.content.Intent;
+import android.net.VpnService;
+
+public class VoidVpnService extends VpnService {
+
+ static final String TAG = VoidVpnService.class.getSimpleName();
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ String action = intent.getAction();
+ if (action == Constants.START_BLOCKING_VPN_PROFILE) {
+ new Thread(new Runnable() {
+ public void run() {
+ Builder builder = new Builder();
+ builder.setSession("Blocking until running");
+ builder.addAddress("10.42.0.8",16);
+ builder.addRoute("0.0.0.0", 1);
+ builder.addRoute("192.168.1.0", 24);
+ builder.addDnsServer("10.42.0.1");
+ try {
+ builder.establish();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ android.util.Log.d(TAG, "VoidVpnService set up");
+ }
+ }).run();
+ }
+ return 0;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
new file mode 100644
index 00000000..6487f6c1
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
@@ -0,0 +1,60 @@
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.util.Log;
+
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+
+import se.leap.bitmaskclient.ConfigHelper;
+
+public class VpnCertificateValidator {
+ public final static String TAG = VpnCertificateValidator.class.getSimpleName();
+
+ public boolean isValid(String certificate) {
+ if(!certificate.isEmpty()) {
+ X509Certificate certificate_x509 = ConfigHelper.parseX509CertificateFromString(certificate);
+ return isValid(certificate_x509);
+ } else return true;
+ }
+
+ private boolean isValid(X509Certificate certificate) {
+ Calendar offset_date = calculateOffsetCertificateValidity(certificate);
+ try {
+ Log.d(TAG, "offset_date = " + offset_date.getTime().toString());
+ certificate.checkValidity(offset_date.getTime());
+ return true;
+ } catch(CertificateExpiredException e) {
+ return false;
+ } catch(CertificateNotYetValidException e) {
+ return false;
+ }
+ }
+
+ private Calendar calculateOffsetCertificateValidity(X509Certificate certificate) {
+ Log.d(TAG, "certificate not after = " + certificate.getNotAfter());
+ long preventive_time = Math.abs(certificate.getNotBefore().getTime() - certificate.getNotAfter().getTime())/2;
+ long current_date_millis = Calendar.getInstance().getTimeInMillis();
+
+ Calendar limit_date = Calendar.getInstance();
+ limit_date.setTimeInMillis(current_date_millis + preventive_time);
+ return limit_date;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
new file mode 100644
index 00000000..0c8e9a04
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
@@ -0,0 +1,145 @@
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Iterator;
+
+import se.leap.bitmaskclient.Provider;
+
+public class VpnConfigGenerator {
+
+ private JSONObject general_configuration;
+ private JSONObject gateway;
+
+ private static SharedPreferences preferences;
+ public final static String TAG = VpnConfigGenerator.class.getSimpleName();
+ private final String new_line = System.getProperty("line.separator"); // Platform new line
+
+ public VpnConfigGenerator(SharedPreferences preferences, JSONObject general_configuration, JSONObject gateway) {
+ this.general_configuration = general_configuration;
+ this.gateway = gateway;
+ VpnConfigGenerator.preferences = preferences;
+ }
+
+ public String generate() {
+ return
+ generalConfiguration()
+ + new_line
+ + gatewayConfiguration()
+ + new_line
+ + secretsConfiguration()
+ + new_line
+ + androidCustomizations();
+ }
+
+ private String generalConfiguration() {
+ String common_options = "";
+ try {
+ Iterator keys = general_configuration.keys();
+ while ( keys.hasNext() ){
+ String key = keys.next().toString();
+
+ common_options += key + " ";
+ for ( String word : general_configuration.getString(key).split(" ") )
+ common_options += word + " ";
+ common_options += new_line;
+
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ common_options += "client";
+
+ return common_options;
+ }
+
+ private String gatewayConfiguration() {
+ String remotes = "";
+
+ String remote = "ip_address";
+ String remote_openvpn_keyword = "remote";
+ String ports = "ports";
+ String protos = "protocols";
+ String capabilities = "capabilities";
+ String udp = "udp";
+
+ try {
+ JSONArray protocolsJSON = gateway.getJSONObject(capabilities).getJSONArray(protos);
+ for ( int i=0; i<protocolsJSON.length(); i++ ) {
+ String remote_line = remote_openvpn_keyword;
+ remote_line += " " + gateway.getString(remote);
+ remote_line += " " + gateway.getJSONObject(capabilities).getJSONArray(ports).optString(0);
+ remote_line += " " + protocolsJSON.optString(i);
+ if(remote_line.endsWith(udp))
+ remotes = remotes.replaceFirst(remote_openvpn_keyword, remote_line + new_line + remote_openvpn_keyword);
+ else
+ remotes += remote_line;
+ remotes += new_line;
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ Log.d(TAG, "remotes = " + remotes);
+ return remotes;
+ }
+
+ private String secretsConfiguration() {
+
+ String ca =
+ "<ca>"
+ + new_line
+ + preferences.getString(Provider.CA_CERT, "")
+ + new_line
+ + "</ca>";
+
+ String key =
+ "<key>"
+ + new_line
+ + preferences.getString(Constants.PRIVATE_KEY, "")
+ + new_line
+ + "</key>";
+
+ String openvpn_cert =
+ "<cert>"
+ + new_line
+ + preferences.getString(Constants.CERTIFICATE, "")
+ + new_line
+ + "</cert>";
+
+ return ca + new_line + key + new_line + openvpn_cert;
+ }
+
+ private String androidCustomizations() {
+ return
+ "remote-cert-tls server"
+ + new_line
+ + "persist-tun"
+ + new_line
+ + "auth-retry nointeract";
+ }
+}