From a3e93c14fbd82d7f4229f30bbcb622e5ddb71983 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Thu, 18 Dec 2014 16:40:07 +0100
Subject: Log out before starting Configuration Wizard.

---
 .../main/java/se/leap/bitmaskclient/Dashboard.java | 28 +++++++++++++---------
 .../java/se/leap/bitmaskclient/EipFragment.java    |  1 +
 2 files changed, 18 insertions(+), 11 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index 52b22695..cf65c9b8 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -81,6 +81,7 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
     private Provider provider;
     private static boolean authed_eip;
     public ProviderAPIResultReceiver providerAPI_result_receiver;
+    private boolean switching_provider;
 
     @Override
     protected void onSaveInstanceState(@NotNull Bundle outState) {
@@ -288,15 +289,11 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 		    startActivity(startLW);
 		    return true;
 		case R.id.switch_provider:
-			if (provider.hasEIP()){
-				if (preferences.getBoolean(Constants.AUTHED_EIP, false)) {
-                    logOut();
-                }
-                eip_fragment.askToStopEIP();
-			}
-			preferences.edit().clear().apply();
-			startActivityForResult(new Intent(this,ConfigurationWizard.class), SWITCH_PROVIDER);
-			return true;
+		    switching_provider = true;
+		    if (preferences.getBoolean(Constants.AUTHED_EIP, false)) {
+			logOut();
+		    } else switchProvider();
+		    return true;
 		case R.id.login_button:
 			logInDialog(Bundle.EMPTY);
 			return true;
@@ -307,7 +304,7 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 			signUpDialog(Bundle.EMPTY);
 			return true;
 		default:
-				return super.onOptionsItemSelected(item);
+		    return super.onOptionsItemSelected(item);
 		}
 		
 	}
@@ -402,6 +399,13 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	startService(provider_API_command);
     }
 
+    private void switchProvider() {
+        if (provider.hasEIP()) eip_fragment.askToStopEIP();
+        preferences.edit().clear().apply();
+        switching_provider = false;
+        startActivityForResult(new Intent(this,ConfigurationWizard.class), SWITCH_PROVIDER);
+    }
+
 	/**
 	 * Asks ProviderAPI to download an authenticated OpenVPN certificate.
 	 */
@@ -456,7 +460,9 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 		
 		authed_eip = false;
 		preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
-
+		
+		if(switching_provider) switchProvider();
+		
 	    } else if(resultCode == ProviderAPI.LOGOUT_FAILED) {
 		changeStatusMessage(resultCode);
 		hideProgressBar();
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
index 420da7a1..59e9ca5a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -211,6 +211,7 @@ public class EipFragment extends Fragment implements Observer {
 
 	String status = parent_activity.getString(R.string.eip_state_not_connected);
 	status_message.setText(status);
+
 	eipCommand(Constants.ACTION_STOP_EIP);
     }
 	
-- 
cgit v1.2.3


From 20049d1dbca7c032f2f88824098d9b39c71712d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Tue, 23 Dec 2014 19:46:58 +0100
Subject: -100 lines on Dashboard, gradle fix.

Gradle doesn't invoke updateIcsOpenVpn task unless the task is
explicitly invoked. I needed to put all the related tasks into the
configuration phase, rather than on the execution one.
---
 .../main/java/se/leap/bitmaskclient/Dashboard.java | 635 +++++++++------------
 .../java/se/leap/bitmaskclient/EipFragment.java    |  10 +-
 2 files changed, 271 insertions(+), 374 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index cf65c9b8..659cd22c 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -17,37 +17,21 @@
 package se.leap.bitmaskclient;
 
 import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.DialogFragment;
-import android.app.FragmentTransaction;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
+import android.app.*;
+import android.content.*;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
+import android.os.*;
 import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.ProgressBar;
-import android.widget.TextView;
+import android.view.*;
+import android.widget.*;
 
 import org.jetbrains.annotations.NotNull;
-import org.json.JSONException;
-import org.json.JSONObject;
+import org.json.*;
+import java.net.*;
 
-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;
+import butterknife.*;
+import de.blinkt.openvpn.activities.*;
+import se.leap.bitmaskclient.eip.*;
 
 /**
  * The main user facing Activity of Bitmask Android, consisting of status, controls,
@@ -58,16 +42,16 @@ import se.leap.bitmaskclient.eip.EipStatus;
  */
 public class Dashboard extends Activity implements SessionDialog.SessionDialogInterface, ProviderAPIResultReceiver.Receiver {
 
-	protected static final int CONFIGURE_LEAP = 0;
-	protected static final int SWITCH_PROVIDER = 1;
+    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 TAG = Dashboard.class.getSimpleName();
+    public static final String SHARED_PREFERENCES = "LEAPPreferences";
+    public static final String ACTION_QUIT = "quit";
     public static final String REQUEST_CODE = "request_code";
     public static final String PARAMETERS = "dashboard parameters";
     public static final String START_ON_BOOT = "dashboard start on boot";
-    final public static String ON_BOOT = "dashboard on boot";
+    public static final String ON_BOOT = "dashboard on boot";
     public static final String APP_VERSION = "bitmask version";
 
     private static Context app;
@@ -83,6 +67,25 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
     public ProviderAPIResultReceiver providerAPI_result_receiver;
     private boolean switching_provider;
 
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+	super.onCreate(savedInstanceState);
+		
+	app = this;
+
+	PRNGFixes.apply();
+
+	preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+	fragment_manager = new FragmentManagerEnhanced(getFragmentManager());
+	handleVersion();
+
+        provider = getSavedProvider(savedInstanceState);
+        if (provider == null || provider.getName().isEmpty())
+	    startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
+	else
+	    buildDashboard(getIntent().getBooleanExtra(ON_BOOT, false));
+    }
+
     @Override
     protected void onSaveInstanceState(@NotNull Bundle outState) {
         if(provider != null)
@@ -90,25 +93,6 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
         super.onSaveInstanceState(outState);
     }
 
-    @Override
-	protected void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		
-		app = this;		
-		
-		PRNGFixes.apply();
-	    
-	    preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
-	    fragment_manager = new FragmentManagerEnhanced(getFragmentManager());
-	    handleVersion();
-
-        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)
@@ -131,113 +115,100 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
         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).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
 	    case 101: // 0.8.0
-		if(!preferences.getString(Constants.KEY, "").isEmpty()) {
-		    Intent rebuildVpnProfiles = new Intent(getApplicationContext(), EIP.class);
-		    rebuildVpnProfiles.setAction(Constants.ACTION_UPDATE_EIP_SERVICE);
-		    startService(rebuildVpnProfiles);
-		}
+		if(!preferences.getString(Constants.KEY, "").isEmpty())
+		    updateEipService();
 		break;
 	    }
 	} catch (NameNotFoundException e) {
-        Log.d(TAG, "Handle version didn't find any " + getPackageName() + " package");
+	    Log.d(TAG, "Handle version didn't find any " + getPackageName() + " package");
 	}
     }
     
     @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)) {
+		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)) {
+		    sessionDialog(Bundle.EMPTY);
+		}
+	    } else if (resultCode == RESULT_CANCELED && data.hasExtra(ACTION_QUIT)) {
                 finish();
-        } else
+	    } else
 		configErrorDialog();
 	} else if(requestCode == EIP.DISCONNECT) {
 	    EipStatus.getInstance().setConnectedOrDisconnected();
 	}
     }
 
-	/**
-	 * Dialog shown when encountering a configuration error.  Such errors require
-	 * reconfiguring LEAP or aborting the application.
-	 */
-	private void configErrorDialog() {
-		AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getAppContext());
-		alertBuilder.setTitle(getResources().getString(R.string.setup_error_title));
-		alertBuilder
-			.setMessage(getResources().getString(R.string.setup_error_text))
-			.setCancelable(false)
-			.setPositiveButton(getResources().getString(R.string.setup_error_configure_button), new DialogInterface.OnClickListener() {
-				@Override
-				public void onClick(DialogInterface dialog, int which) {
-					startActivityForResult(new Intent(getAppContext(),ConfigurationWizard.class),CONFIGURE_LEAP);
-				}
-			})
-			.setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() {
-				@Override
-				public void onClick(DialogInterface dialog, int which) {
-				    preferences.edit().remove(Provider.KEY).remove(Constants.PROVIDER_CONFIGURED).apply();
-					finish();
-				}
-			})
-			.show();
-	}
+    private void configErrorDialog() {
+	AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getAppContext());
+	alertBuilder.setTitle(getResources().getString(R.string.setup_error_title));
+	alertBuilder
+	    .setMessage(getResources().getString(R.string.setup_error_text))
+	    .setCancelable(false)
+	    .setPositiveButton(getResources().getString(R.string.setup_error_configure_button), new DialogInterface.OnClickListener() {
+		    @Override
+		    public void onClick(DialogInterface dialog, int which) {
+			startActivityForResult(new Intent(getAppContext(),ConfigurationWizard.class),CONFIGURE_LEAP);
+		    }
+		})
+	    .setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() {
+		    @Override
+		    public void onClick(DialogInterface dialog, int which) {
+			preferences.edit().remove(Provider.KEY).remove(Constants.PROVIDER_CONFIGURED).apply();
+			finish();
+		    }
+		})
+	    .show();
+    }
 	
-	/**
-	 * Inflates permanent UI elements of the View and contains logic for what
-	 * service dependent UI elements to include.
-	 */
-	private void buildDashboard(boolean hide_and_turn_on_eip) {
-		setContentView(R.layout.dashboard);
+    /**
+     * Inflates permanent UI elements of the View and contains logic for what
+     * service dependent UI elements to include.
+     */
+    private void buildDashboard(boolean hide_and_turn_on_eip) {
+	setContentView(R.layout.dashboard);
         ButterKnife.inject(this);
 
-		provider_name.setText(provider.getDomain());
-		if ( provider.hasEIP()){
-
+	provider_name.setText(provider.getDomain());
+	if ( provider.hasEIP()){
             fragment_manager.removePreviousFragment(EipFragment.TAG);
             eip_fragment = new EipFragment();
 
-		    if (hide_and_turn_on_eip) {
-			preferences.edit().remove(Dashboard.START_ON_BOOT).apply();
-			Bundle arguments = new Bundle();
-			arguments.putBoolean(EipFragment.START_ON_BOOT, true);
+	    if (hide_and_turn_on_eip) {
+		preferences.edit().remove(Dashboard.START_ON_BOOT).apply();
+		Bundle arguments = new Bundle();
+		arguments.putBoolean(EipFragment.START_ON_BOOT, true);
                 if(eip_fragment != null) eip_fragment.setArguments(arguments);
-		    }
+	    }
 
             fragment_manager.replace(R.id.servicesCollection, eip_fragment, EipFragment.TAG);
-
-		    if (hide_and_turn_on_eip) {
-			onBackPressed();
-		    }
-		}
+	    if (hide_and_turn_on_eip) {
+		onBackPressed();
+	    }
 	}
+    }
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
@@ -268,110 +239,104 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	}
 	return true;
     }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+	getMenuInflater().inflate(R.menu.client_dashboard, menu);
+	return true;
+    }
 	
-	@Override
-	public boolean onCreateOptionsMenu(Menu menu) {
-		getMenuInflater().inflate(R.menu.client_dashboard, menu);
-		return true;
-	}
-	
-	@Override
-	public boolean onOptionsItemSelected(MenuItem item){
-		Intent intent;
-		switch (item.getItemId()){
-		case R.id.about_leap:
-			intent = new Intent(this, AboutActivity.class);
-			startActivity(intent);
-			return true;
-		case R.id.log_window:
-		    Intent startLW = new Intent(getAppContext(), LogWindow.class);
-		    startLW.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
-		    startActivity(startLW);
-		    return true;
-		case R.id.switch_provider:
-		    switching_provider = true;
-		    if (preferences.getBoolean(Constants.AUTHED_EIP, false)) {
-			logOut();
-		    } else switchProvider();
-		    return true;
-		case R.id.login_button:
-			logInDialog(Bundle.EMPTY);
-			return true;
-		case R.id.logout_button:
-			logOut();
-			return true;
-		case R.id.signup_button:
-			signUpDialog(Bundle.EMPTY);
-			return true;
-		default:
-		    return super.onOptionsItemSelected(item);
-		}
-		
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item){
+	Intent intent;
+	switch (item.getItemId()){
+	case R.id.about_leap:
+	    intent = new Intent(this, AboutActivity.class);
+	    startActivity(intent);
+	    return true;
+	case R.id.log_window:
+	    Intent startLW = new Intent(getAppContext(), LogWindow.class);
+	    startLW.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+	    startActivity(startLW);
+	    return true;
+	case R.id.switch_provider:
+	    switching_provider = true;
+	    if (preferences.getBoolean(Constants.AUTHED_EIP, false)) logOut();
+	    else switchProvider();
+	    return true;
+	case R.id.login_button:
+	    sessionDialog(Bundle.EMPTY);
+	    return true;
+	case R.id.logout_button:
+	    logOut();
+	    return true;
+	case R.id.signup_button:
+	    sessionDialog(Bundle.EMPTY);
+	    return true;
+	default:
+	    return super.onOptionsItemSelected(item);
 	}
-
-    protected Intent prepareProviderAPICommand() {
-	providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
-	providerAPI_result_receiver.setReceiver(this);
-		
-	Intent command = new Intent(this, ProviderAPI.class);
-
-	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() && fragment_manager.findFragmentByTag(SessionDialog.TAG) == null)
-	    newFragment.setArguments(resultData);
-	newFragment.show(transaction, SessionDialog.TAG);
+    @Override
+    public void signUp(String username, String password) {
+	Bundle parameters = bundleParameters(username, password);
+	providerApiCommand(parameters, R.string.signingup_message, ProviderAPI.SRP_REGISTER);
     }
 
     @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);
+	Bundle parameters = bundleParameters(username, password);
+	providerApiCommand(parameters, R.string.authenticating_message, ProviderAPI.SRP_AUTH);
+    }
+	
+    public void logOut() {
+	providerApiCommand(Bundle.EMPTY, R.string.logout_message, ProviderAPI.LOG_OUT);
+    }
+
+    private void downloadAuthedUserCertificate() {
+	Bundle parameters = new Bundle();
+	parameters.putString(ConfigurationWizard.TYPE_OF_CERTIFICATE, ConfigurationWizard.AUTHED_CERTIFICATE);
+	
+	providerApiCommand(parameters, R.string.downloading_certificate_message, ProviderAPI.DOWNLOAD_CERTIFICATE);
+    }
+
+    private Bundle bundleParameters(String username, String password) {
+        Bundle parameters = new Bundle();
+	if(!username.isEmpty() && !password.isEmpty()) {
+	    parameters.putString(SessionDialog.USERNAME, username);
+	    parameters.putString(SessionDialog.PASSWORD, password);
+	}
+	return parameters;
+    }
 
+    private void providerApiCommand(Bundle parameters, int progressbar_message_resId, String providerApi_action) {
         if(eip_fragment != null) {
             eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
-            eip_fragment.status_message.setText(R.string.authenticating_message);
+            setStatusMessage(progressbar_message_resId);
         }
-	provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
-	provider_API_command.setAction(ProviderAPI.SRP_AUTH);
-	startService(provider_API_command);
+	
+	Intent command = prepareProviderAPICommand(parameters, providerApi_action);
+	startService(command);
     }
 
-    public void cancelLoginOrSignup() {
-      EipStatus.getInstance().setConnectedOrDisconnected();
-    }
+    protected Intent prepareProviderAPICommand(Bundle parameters, String action) {
+	providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+	providerAPI_result_receiver.setReceiver(this);
 	
-    /**
-     * Asks ProviderAPI to log out.
-     */
-    public void logOut() {
-	Intent provider_API_command = prepareProviderAPICommand();
-        if(eip_fragment != null) {
+	Intent command = new Intent(this, ProviderAPI.class);
+	
+	command.putExtra(ProviderAPI.PARAMETERS, parameters);
+	command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
+	command.setAction(action);
+	return command;
+    }
 
-            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);
+    public void cancelLoginOrSignup() {
+      EipStatus.getInstance().setConnectedOrDisconnected();
     }
     
-    /**
-     * Shows the sign up dialog.
-     */
-    public void signUpDialog(Bundle resultData) {
+    public void sessionDialog(Bundle resultData) {
 	FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG);
 
 	DialogFragment newFragment = SessionDialog.newInstance();
@@ -381,169 +346,118 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	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);
-    }
-
     private void switchProvider() {
         if (provider.hasEIP()) eip_fragment.askToStopEIP();
+	
         preferences.edit().clear().apply();
         switching_provider = false;
-        startActivityForResult(new Intent(this,ConfigurationWizard.class), SWITCH_PROVIDER);
+        startActivityForResult(new Intent(this, ConfigurationWizard.class), SWITCH_PROVIDER);
     }
 
-	/**
-	 * Asks ProviderAPI to download an authenticated OpenVPN certificate.
-	 */
-	private void downloadAuthedUserCertificate() {
-		providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
-		providerAPI_result_receiver.setReceiver(this);
-		
-		Intent provider_API_command = new Intent(this, ProviderAPI.class);
-
-		Bundle parameters = new Bundle();
-		parameters.putString(ConfigurationWizard.TYPE_OF_CERTIFICATE, ConfigurationWizard.AUTHED_CERTIFICATE);
-
-		provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
-		provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
-		provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
-		
-		startService(provider_API_command);
+    @Override
+    public void onReceiveResult(int resultCode, Bundle resultData) {
+	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) {
+	    updateViewHidingProgressBar(resultCode);
+	    sessionDialog(resultData);
+	} else if(resultCode == ProviderAPI.SUCCESSFUL_LOGIN) {
+	    authed_eip = true;
+	    preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
+
+	    updateViewHidingProgressBar(resultCode);
+	    downloadAuthedUserCertificate();
+	} else if(resultCode == ProviderAPI.FAILED_LOGIN) {
+	    updateViewHidingProgressBar(resultCode);
+	    sessionDialog(resultData);
+	} else if(resultCode == ProviderAPI.SUCCESSFUL_LOGOUT) {
+	    authed_eip = false;
+	    preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
+
+	    updateViewHidingProgressBar(resultCode);
+	    if(switching_provider) switchProvider();
+	} else if(resultCode == ProviderAPI.LOGOUT_FAILED) {
+	    updateViewHidingProgressBar(resultCode);
+	    setResult(RESULT_CANCELED);
+	} else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) {
+	    updateViewHidingProgressBar(resultCode);
+	    updateEipService();
+	    setResult(RESULT_OK);
+	} else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
+	    updateViewHidingProgressBar(resultCode);
+	    setResult(RESULT_CANCELED);
 	}
+	else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) {
+	    setResult(RESULT_OK);
 
-	@Override
-	public void onReceiveResult(int resultCode, Bundle resultData) {
-	    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();
-		
-		if(switching_provider) switchProvider();
-		
-	    } 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);
-		hideProgressBar();
-        	setResult(RESULT_CANCELED);
-	    }
-        else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) {
-        setResult(RESULT_OK);
+	    updateEipService();
+	} else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) {
+	    setResult(RESULT_CANCELED);
+	}
+    }
 
-        updateEipService();
-        } else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) {
-        setResult(RESULT_CANCELED);
+    private void updateViewHidingProgressBar(int resultCode) {
+	changeStatusMessage(resultCode);
+	hideProgressBar();
+	invalidateOptionsMenu();
     }
-	}
 
     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) {
+	startService(updateEIP);
+    }
+
+    private void changeStatusMessage(final int previous_result_code) {
+	ResultReceiver status_receiver = new ResultReceiver(new Handler()){
+		protected void onReceiveResult(int resultCode, Bundle resultData){
+		    super.onReceiveResult(resultCode, 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();
+		    if (request.equalsIgnoreCase(Constants.ACTION_IS_EIP_RUNNING)){
+			if (resultCode == Activity.RESULT_OK){
+			    switch(previous_result_code){
+			    case ProviderAPI.SUCCESSFUL_LOGIN: setStatusMessage(R.string.succesful_authentication_message); break;
+			    case ProviderAPI.FAILED_LOGIN: setStatusMessage(R.string.authentication_failed_message); break;
+			    case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: setStatusMessage(R.string.authed_secured_status); break;
+			    case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: setStatusMessage(R.string.incorrectly_downloaded_certificate_message); break;
+			    case ProviderAPI.SUCCESSFUL_LOGOUT: setStatusMessage(R.string.logged_out_message); break;
+			    case ProviderAPI.LOGOUT_FAILED: setStatusMessage(R.string.log_out_failed_message); break;
+						
+			    }	
+			}
+			else if(resultCode == Activity.RESULT_CANCELED){
+			    switch(previous_result_code){
+			    case ProviderAPI.SUCCESSFUL_LOGIN: setStatusMessage(R.string.succesful_authentication_message); break;
+			    case ProviderAPI.FAILED_LOGIN: setStatusMessage(R.string.authentication_failed_message); break;
+			    case ProviderAPI.FAILED_SIGNUP: setStatusMessage(R.string.registration_failed_message); break;
+			    case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: break;
+			    case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: setStatusMessage(R.string.incorrectly_downloaded_certificate_message); break;
+			    case ProviderAPI.SUCCESSFUL_LOGOUT: setStatusMessage(R.string.logged_out_message); break;
+			    case ProviderAPI.LOGOUT_FAILED: setStatusMessage(R.string.log_out_failed_message); break;			
+			    }
 			}
 		    }
+					
 		}
 	    };
-	//updateEIP.putExtra(Constants.RECEIVER_TAG, receiver);
-	startService(updateEIP);
+	eipIsRunning(status_receiver);		
     }
 
-	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(Constants.REQUEST_TAG);
-				if (request.equalsIgnoreCase(Constants.ACTION_IS_EIP_RUNNING)){
-					if (resultCode == Activity.RESULT_OK){
-
-						switch(previous_result_code){
-						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;
-						
-						}	
-					}
-					else if(resultCode == Activity.RESULT_CANCELED){
-
-						switch(previous_result_code){
-
-						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: 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;			
-						}
-					}
-				}
-					
-			}
-		};
-		eipIsRunning(eip_status_receiver);		
-	}
+    private void setStatusMessage(int string_resId) {
+	if(eip_fragment != null && eip_fragment.status_message != null)
+	    eip_fragment.status_message.setText(string_resId);
+    }
+
+    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 intent = new Intent(this, EIP.class);
+	intent.setAction(Constants.ACTION_IS_EIP_RUNNING);
+	intent.putExtra(Constants.RECEIVER_TAG, eip_receiver);
+	startService(intent);
+    }
 
     private void hideProgressBar() {
         if(eip_fragment != null) {
@@ -552,28 +466,13 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
         }
     }
 
-	/**
-	 * For retrieving the base application Context in classes that don't extend
-	 * Android's Activity class
-	 * 
-	 * @return Application Context as defined by <code>this</code> for Dashboard instance
-	 */
-	public static Context getAppContext() {
-		return app;
-	}
-
-	
+    public static Context getAppContext() {
+	return app;
+    }
+    
     @Override
     public void startActivityForResult(Intent intent, int requestCode) {
         intent.putExtra(Dashboard.REQUEST_CODE, requestCode);
         super.startActivityForResult(intent, requestCode);
     }
-
-	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(Constants.ACTION_IS_EIP_RUNNING);
-		eip_intent.putExtra(Constants.RECEIVER_TAG, eip_receiver);
-		startService(eip_intent);
-	}
 }
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
index 59e9ca5a..27c70c43 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -56,8 +56,7 @@ public class EipFragment extends Fragment implements Observer {
 	parent_activity = activity;
 
         Dashboard dashboard = (Dashboard) parent_activity;
-        Intent provider_API_command = dashboard.prepareProviderAPICommand();
-        provider_API_command.setAction(ProviderAPI.DOWNLOAD_EIP_SERVICE);
+        Intent provider_API_command = dashboard.prepareProviderAPICommand(Bundle.EMPTY, ProviderAPI.DOWNLOAD_EIP_SERVICE);
         parent_activity.startService(provider_API_command);
     }
     
@@ -139,7 +138,7 @@ public class EipFragment extends Fragment implements Observer {
 	    Dashboard dashboard = (Dashboard) parent_activity;
         Bundle bundle = new Bundle();
         bundle.putBoolean(IS_PENDING, true);
-	    dashboard.logInDialog(bundle);
+	    dashboard.sessionDialog(bundle);
 	}	    
     }
     
@@ -344,10 +343,9 @@ public class EipFragment extends Fragment implements Observer {
 		    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);
+			dashboard.sessionDialog(Bundle.EMPTY);
 		    } else {
-			Intent provider_API_command = dashboard.prepareProviderAPICommand();
-			provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
+			Intent provider_API_command = dashboard.prepareProviderAPICommand(Bundle.EMPTY, ProviderAPI.DOWNLOAD_CERTIFICATE);
 			parent_activity.startService(provider_API_command);
 		    }
 		    break;
-- 
cgit v1.2.3


From b1219330faf64b2cd4330d44390fdec137b659b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Wed, 17 Dec 2014 19:08:53 +0100
Subject: Add remotes for each port, udp before tcp in each.

---
 .../leap/bitmaskclient/eip/VpnConfigGenerator.java | 38 ++++++++++++----------
 1 file changed, 21 insertions(+), 17 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
index 0c8e9a04..a320bee5 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
@@ -79,32 +79,36 @@ public class VpnConfigGenerator {
     private String gatewayConfiguration() {
 	String remotes = "";
 		
-	String remote = "ip_address";
-	String remote_openvpn_keyword = "remote";
-	String ports = "ports";
-	String protos = "protocols";
-	String capabilities = "capabilities";
+	String ip_address_keyword = "ip_address";
+	String remote_keyword = "remote";
+	String ports_keyword = "ports";
+	String protocol_keyword = "protocols";
+	String capabilities_keyword = "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;
+	    String ip_address = gateway.getString(ip_address_keyword);
+	    JSONObject capabilities = gateway.getJSONObject(capabilities_keyword);
+	    JSONArray ports = capabilities.getJSONArray(ports_keyword);
+	    for (int i=0; i<ports.length(); i++) {
+		String port_specific_remotes = "";
+		int port = ports.getInt(i);
+		JSONArray protocols = capabilities.getJSONArray(protocol_keyword);
+		for ( int j=0; j<protocols.length(); j++ ) {
+		    String protocol = protocols.optString(j);
+		    String new_remote = remote_keyword + " " + ip_address + " " + port + " " + protocol + new_line;
+
+		    port_specific_remotes = protocol.equalsIgnoreCase(udp) ?
+			port_specific_remotes.replaceFirst(remote_keyword, new_remote + new_line + remote_keyword) :
+			new_remote;
+		}
+		remotes += port_specific_remotes;
 	    }
 	} catch (JSONException e) {
 	    // TODO Auto-generated catch block
 	    e.printStackTrace();
 	}
 		
-	Log.d(TAG, "remotes = " + remotes);
 	return remotes;
     }
 
-- 
cgit v1.2.3


From d6190becb1c48ee912b11a4206116d0fd4c90772 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Sat, 20 Dec 2014 20:14:23 +0100
Subject: Update ics-openvpn to 1006

---
 app/src/main/java/de/blinkt/openvpn/LaunchVPN.java |   2 +-
 .../main/java/de/blinkt/openvpn/VpnProfile.java    | 127 ++++++++--
 .../blinkt/openvpn/activities/DisconnectVPN.java   |   2 +-
 .../de/blinkt/openvpn/activities/LogWindow.java    |   2 +-
 .../main/java/de/blinkt/openvpn/core/CIDRIP.java   |   2 +-
 .../java/de/blinkt/openvpn/core/ConfigParser.java  | 280 +++++++++++++--------
 .../java/de/blinkt/openvpn/core/Connection.java    |  51 ++++
 .../blinkt/openvpn/core/DeviceStateReceiver.java   |  12 +-
 .../blinkt/openvpn/core/ICSOpenVPNApplication.java |   2 +-
 .../openvpn/core/LollipopDeviceStateListener.java  |  53 ++++
 .../java/de/blinkt/openvpn/core/NativeUtils.java   |   3 +-
 .../java/de/blinkt/openvpn/core/NetworkSpace.java  |  11 +-
 .../de/blinkt/openvpn/core/OpenVPNManagement.java  |   2 +-
 .../de/blinkt/openvpn/core/OpenVPNService.java     | 153 +++++++++--
 .../java/de/blinkt/openvpn/core/OpenVPNThread.java |   2 +-
 .../openvpn/core/OpenVpnManagementThread.java      |   4 +-
 .../java/de/blinkt/openvpn/core/PRNGFixes.java     |   2 +-
 .../de/blinkt/openvpn/core/ProfileManager.java     |   2 +-
 .../de/blinkt/openvpn/core/ProxyDetection.java     |   2 +-
 .../de/blinkt/openvpn/core/VPNLaunchHelper.java    |   3 +-
 .../java/de/blinkt/openvpn/core/VpnStatus.java     |   8 +-
 .../java/de/blinkt/openvpn/core/X509Utils.java     |   2 +-
 .../de/blinkt/openvpn/fragments/LogFragment.java   |   2 +-
 .../java/de/blinkt/openvpn/views/SeekBarTicks.java |   2 +-
 24 files changed, 560 insertions(+), 171 deletions(-)
 create mode 100644 app/src/main/java/de/blinkt/openvpn/core/Connection.java
 create mode 100644 app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
index d7f3e110..02abd7a1 100644
--- a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn;
diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
index fb2ba90d..4f747d21 100644
--- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
+++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn;
@@ -42,6 +42,7 @@ import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Locale;
 import java.util.UUID;
 import java.util.Vector;
@@ -51,13 +52,14 @@ import javax.crypto.Cipher;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;
 
+import de.blinkt.openvpn.core.Connection;
 import de.blinkt.openvpn.core.NativeUtils;
 import de.blinkt.openvpn.core.OpenVPNService;
 import de.blinkt.openvpn.core.VPNLaunchHelper;
 import de.blinkt.openvpn.core.VpnStatus;
 import de.blinkt.openvpn.core.X509Utils;
 
-public class VpnProfile implements Serializable {
+public class VpnProfile implements Serializable, Cloneable {
     // Note that this class cannot be moved to core where it belongs since
     // the profile loading depends on it being here
     // The Serializable documentation mentions that class name change are possible
@@ -71,7 +73,7 @@ public class VpnProfile implements Serializable {
 
     private static final long serialVersionUID = 7085688938959334563L;
     public static final int MAXLOGLEVEL = 4;
-    public static final int CURRENT_PROFILE_VERSION = 2;
+    public static final int CURRENT_PROFILE_VERSION = 5;
     public static final int DEFAULT_MSSFIX_SIZE = 1450;
     public static String DEFAULT_DNS1 = "8.8.8.8";
     public static String DEFAULT_DNS2 = "8.8.4.4";
@@ -106,12 +108,10 @@ public class VpnProfile implements Serializable {
     public String mClientKeyFilename;
     public String mCaFilename;
     public boolean mUseLzo = true;
-    public String mServerPort = "1194";
-    public boolean mUseUdp = true;
     public String mPKCS12Filename;
     public String mPKCS12Password;
     public boolean mUseTLSAuth = false;
-    public String mServerName = "openvpn.blinkt.de";
+
     public String mDNS1 = DEFAULT_DNS1;
     public String mDNS2 = DEFAULT_DNS2;
     public String mIPv4Address;
@@ -152,6 +152,16 @@ public class VpnProfile implements Serializable {
     public String mExcludedRoutes;
     public String mExcludedRoutesv6;
     public int mMssFix =0; // -1 is default,
+    public Connection[] mConnections = new Connection[0];
+    public boolean mRemoteRandom=false;
+    public HashSet<String> mAllowedAppsVpn = new HashSet<String>();
+    public boolean mAllowedAppsVpnAreDisallowed = true;
+
+
+    /* Options no long used in new profiles */
+    public String mServerName = "openvpn.blinkt.de";
+    public String mServerPort = "1194";
+    public boolean mUseUdp = true;
 
 
 
@@ -159,6 +169,9 @@ public class VpnProfile implements Serializable {
         mUuid = UUID.randomUUID();
         mName = name;
         mProfileVersion = CURRENT_PROFILE_VERSION;
+
+        mConnections = new Connection[1];
+        mConnections[0]  = new Connection();
     }
 
     public static String openVpnEscape(String unescaped) {
@@ -206,7 +219,30 @@ public class VpnProfile implements Serializable {
             mAllowLocalLAN = Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT;
         }
 
+        if (mProfileVersion < 4) {
+            moveOptionsToConnection();
+            mAllowedAppsVpnAreDisallowed=true;
+        }
+        if (mAllowedAppsVpn==null)
+            mAllowedAppsVpn = new HashSet<String>();
+        if (mConnections ==null)
+            mConnections = new Connection[0];
+
         mProfileVersion= CURRENT_PROFILE_VERSION;
+
+    }
+
+    private void moveOptionsToConnection() {
+        mConnections = new Connection[1];
+        Connection conn = new Connection();
+
+        conn.mServerName = mServerName;
+        conn.mServerPort = mServerPort;
+        conn.mUseUdp = mUseUdp;
+        conn.mCustomConfiguration = "";
+
+        mConnections[0] = conn;
+
     }
 
     public String getConfigFile(Context context, boolean configForOvpn3) {
@@ -267,15 +303,27 @@ public class VpnProfile implements Serializable {
         // We cannot use anything else than tun
         cfg += "dev tun\n";
 
-        // Server Address
-        cfg += "remote ";
-        cfg += mServerName;
-        cfg += " ";
-        cfg += mServerPort;
-        if (mUseUdp)
-            cfg += " udp\n";
-        else
-            cfg += " tcp-client\n";
+
+        boolean canUsePlainRemotes = true;
+
+        if (mConnections.length==1) {
+            cfg += mConnections[0].getConnectionBlock();
+        } else {
+            for (Connection conn : mConnections) {
+                canUsePlainRemotes = canUsePlainRemotes && conn.isOnlyRemote();
+            }
+
+            if (mRemoteRandom)
+                cfg+="remote-random\n";
+
+            if (canUsePlainRemotes) {
+                for (Connection conn : mConnections) {
+                    if (conn.mEnabled) {
+                        cfg += conn.getConnectionBlock();
+                    }
+                }
+            }
+        }
 
 
         switch (mAuthenticationType) {
@@ -365,11 +413,6 @@ public class VpnProfile implements Serializable {
             }
         }
 
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT && !mAllowLocalLAN)
-            cfg+="redirect-private block-local\n";
-        else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && mAllowLocalLAN)
-            cfg+="redirect-private unblock-local\n";
-
 
         if (mUseDefaultRoutev6)
             cfg += "route-ipv6 ::/0\n";
@@ -405,7 +448,7 @@ public class VpnProfile implements Serializable {
         if (mAuthenticationType != TYPE_STATICKEYS) {
             if (mCheckRemoteCN) {
                 if (mRemoteCN == null || mRemoteCN.equals(""))
-                    cfg += "verify-x509-name " + mServerName + " name\n";
+                    cfg += "verify-x509-name " + mConnections[0].mServerName + " name\n";
                 else
                     switch (mX509AuthType) {
 
@@ -470,6 +513,19 @@ public class VpnProfile implements Serializable {
 
         }
 
+        if (!canUsePlainRemotes) {
+            cfg += "# Connection Options are at the end to allow global options (and global custom options) to influence connection blocks\n";
+            for (Connection conn : mConnections) {
+                if (conn.mEnabled) {
+                    cfg += "<connection>\n";
+                    cfg += conn.getConnectionBlock();
+                    cfg += "</connection>\n";
+                }
+            }
+        }
+
+
+
 
         return cfg;
     }
@@ -639,6 +695,27 @@ public class VpnProfile implements Serializable {
         }
     }
 
+    @Override
+    protected VpnProfile clone() throws CloneNotSupportedException {
+        VpnProfile copy = (VpnProfile) super.clone();
+        copy.mUuid = UUID.randomUUID();
+        copy.mConnections = mConnections.clone();
+        copy.mAllowedAppsVpn = (HashSet<String>) mAllowedAppsVpn.clone();
+        return copy;
+    }
+
+    public VpnProfile copy(String name) {
+        try {
+            VpnProfile copy = (VpnProfile) clone();
+            copy.mName = name;
+            return copy;
+
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
 
     class NoCertReturnedException extends Exception {
         public NoCertReturnedException (String msg) {
@@ -769,6 +846,14 @@ public class VpnProfile implements Serializable {
         if (!mUseDefaultRoute && (getCustomRoutes(mCustomRoutes) == null || getCustomRoutes(mExcludedRoutes) ==null))
             return R.string.custom_route_format_error;
 
+        boolean noRemoteEnabled = true;
+        for (Connection c : mConnections)
+            if (c.mEnabled)
+                noRemoteEnabled = false;
+
+        if(noRemoteEnabled)
+            return R.string.remote_no_server_selected;
+
         // Everything okay
         return R.string.no_error_found;
 
diff --git a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java b/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
index 4940d5d6..dfd815e4 100644
--- a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.activities;
diff --git a/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java b/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java
index 5e4f9517..45f09c8e 100644
--- a/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java
+++ b/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.activities;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java b/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java
index ac9a8ccb..e525abd5 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
index 0d8230b7..5dc96bbc 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
@@ -1,13 +1,17 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
 
+import android.text.TextUtils;
+import android.util.Pair;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.Reader;
+import java.io.StringReader;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Locale;
@@ -28,48 +32,49 @@ public class ConfigParser {
 	private HashMap<String, Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>();
 	private HashMap<String, Vector<String>> meta = new HashMap<String, Vector<String>>();
 
-
-	private boolean extraRemotesAsCustom=false;
-
 	public void parseConfig(Reader reader) throws IOException, ConfigParseError {
 
 
-		BufferedReader br =new BufferedReader(reader);
+        BufferedReader br = new BufferedReader(reader);
 
-        int lineno=0;
-		while (true){
-			String line = br.readLine();
-            lineno++;
-			if(line==null)
-				break;
+        int lineno = 0;
+        try {
+            while (true) {
+                String line = br.readLine();
+                lineno++;
+                if (line == null)
+                    break;
 
-            if (lineno==1 && (line.startsWith("PK\003\004")
-                        ||   (line.startsWith("PK\007\008"))))
+                if (lineno == 1 && (line.startsWith("PK\003\004")
+                        || (line.startsWith("PK\007\008"))))
                     throw new ConfigParseError("Input looks like a ZIP Archive. Import is only possible for OpenVPN config files (.ovpn/.conf)");
 
-			// Check for OpenVPN Access Server Meta information
-			if (line.startsWith("# OVPN_ACCESS_SERVER_")) {
-				Vector<String> metaarg = parsemeta(line);
-				meta.put(metaarg.get(0),metaarg);
-				continue;
-			}
-			Vector<String> args = parseline(line);	
+                // Check for OpenVPN Access Server Meta information
+                if (line.startsWith("# OVPN_ACCESS_SERVER_")) {
+                    Vector<String> metaarg = parsemeta(line);
+                    meta.put(metaarg.get(0), metaarg);
+                    continue;
+                }
+                Vector<String> args = parseline(line);
 
-			if(args.size() ==0)
-				continue;
+                if (args.size() == 0)
+                    continue;
 
 
-			if(args.get(0).startsWith("--"))
-				args.set(0, args.get(0).substring(2));
+                if (args.get(0).startsWith("--"))
+                    args.set(0, args.get(0).substring(2));
 
-			checkinlinefile(args,br);
+                checkinlinefile(args, br);
 
-			String optionname = args.get(0);
-			if(!options.containsKey(optionname)) {
-				options.put(optionname, new Vector<Vector<String>>());
-			}
-			options.get(optionname).add(args);
-		}
+                String optionname = args.get(0);
+                if (!options.containsKey(optionname)) {
+                    options.put(optionname, new Vector<Vector<String>>());
+                }
+                options.get(optionname).add(args);
+            }
+        } catch (java.lang.OutOfMemoryError memoryError) {
+            throw new ConfigParseError("File too large to parse: " + memoryError.getLocalizedMessage());
+        }
 	}
 
 	private Vector<String> parsemeta(String line) {
@@ -98,7 +103,7 @@ public class ConfigParser {
 					break;
 				else {
 					inlinefile+=line;
-					inlinefile+= "\n";					
+					inlinefile+= "\n";
 				}
 			} while(true);
 
@@ -132,7 +137,7 @@ public class ConfigParser {
 
 	// adapted openvpn's parse function to java
 	private Vector<String> parseline(String line) throws ConfigParseError {
-		Vector<String> parameters = new Vector<String>(); 
+		Vector<String> parameters = new Vector<String>();
 
 		if (line.length()==0)
 			return parameters;
@@ -145,12 +150,12 @@ public class ConfigParser {
 		int pos=0;
 		String currentarg="";
 
-		do { 
+		do {
 			// Emulate the c parsing ...
 			char in;
 			if(pos < line.length())
 				in = line.charAt(pos);
-			else 
+			else
 				in = '\0';
 
 			if (!backslash && in == '\\' && state != linestate.readin_single_quote)
@@ -228,10 +233,7 @@ public class ConfigParser {
 	}
 
 
-	final String[] unsupportedOptions = { "config", 
-			"connection", 
-			"proto-force", 
-			"remote-random",
+	final String[] unsupportedOptions = { "config",
 			"tls-server"
 
 	};
@@ -299,7 +301,7 @@ public class ConfigParser {
             "remote",
             "float",
             "port",
-//            "connect-retry",
+            "connect-retry",
             "connect-timeout",
             "connect-retry-max",
             "link-mtu",
@@ -325,7 +327,7 @@ public class ConfigParser {
 
     // This method is far too long
 	@SuppressWarnings("ConstantConditions")
-    public VpnProfile convertProfile() throws ConfigParseError{
+    public VpnProfile convertProfile() throws ConfigParseError, IOException {
 		boolean noauthtypeset=true;
 		VpnProfile np = new VpnProfile(CONVERTED_PROFILE);
 		// Pull, client, tls-client
@@ -338,7 +340,7 @@ public class ConfigParser {
 		}
 
 		Vector<String> secret = getOption("secret", 1, 2);
-		if(secret!=null) 
+		if(secret!=null)
 		{
 			np.mAuthenticationType=VpnProfile.TYPE_STATICKEYS;
 			noauthtypeset=false;
@@ -362,7 +364,7 @@ public class ConfigParser {
                 if (route.size() >= 4)
                     gateway = route.get(3);
 
-				String net = route.get(1);	
+				String net = route.get(1);
 				try {
 					CIDRIP cidr = new CIDRIP(net, netmask);
                     if (gateway.equals("net_gateway"))
@@ -398,7 +400,7 @@ public class ConfigParser {
 		Vector<Vector<String>> tlsauthoptions = getAllOption("tls-auth", 1, 2);
 		if(tlsauthoptions!=null) {
 			for(Vector<String> tlsauth:tlsauthoptions) {
-				if(tlsauth!=null) 
+				if(tlsauth!=null)
 				{
 					if(!tlsauth.get(1).equals("[inline]")) {
 						np.mTLSAuthFilename=tlsauth.get(1);
@@ -458,36 +460,6 @@ public class ConfigParser {
 				throw new ConfigParseError("Invalid mode for --mode specified, need p2p");
 		}
 
-		Vector<String> port = getOption("port", 1,1);
-		if(port!=null){
-			np.mServerPort = port.get(1);
-		}
-
-        Vector<String> rport = getOption("rport", 1,1);
-        if(rport!=null){
-            np.mServerPort = rport.get(1);
-        }
-
-        Vector<String> proto = getOption("proto", 1,1);
-		if(proto!=null){
-			np.mUseUdp=isUdpProto(proto.get(1));
-		}
-
-		// Parse remote config
-		Vector<Vector<String>> remotes = getAllOption("remote",1,3);
-
-		if(remotes!=null && remotes.size()>=1 ) {
-			Vector<String> remote = remotes.get(0);
-			switch (remote.size()) {
-			case 4:
-				np.mUseUdp=isUdpProto(remote.get(3));
-			case 3:
-				np.mServerPort = remote.get(2);
-			case 2:
-				np.mServerName = remote.get(1);
-			}
-		}
-
 
 
 		Vector<Vector<String>> dhcpoptions = getAllOption("dhcp-option", 2, 2);
@@ -581,18 +553,18 @@ public class ConfigParser {
 		if(verifyx509name!=null){
 			np.mRemoteCN = verifyx509name.get(1);
 			np.mCheckRemoteCN=true;
-			if(verifyx509name.size()>2) {  
+			if(verifyx509name.size()>2) {
 				if (verifyx509name.get(2).equals("name"))
 					np.mX509AuthType=VpnProfile.X509_VERIFY_TLSREMOTE_RDN;
 				else if (verifyx509name.get(2).equals("name-prefix"))
 					np.mX509AuthType=VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX;
-				else 
+				else
 					throw new ConfigParseError("Unknown parameter to x509-verify-name: " + verifyx509name.get(2) );
 			} else {
 				np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_DN;
 			}
 
-		} 
+		}
 
 
 		Vector<String> verb = getOption("verb",1,1);
@@ -615,7 +587,7 @@ public class ConfigParser {
 		if(connectretrymax!=null)
 			np.mConnectRetryMax =connectretrymax.get(1);
 
-		Vector<Vector<String>> remotetls = getAllOption("remote-cert-tls", 1, 1);
+        Vector<Vector<String>> remotetls = getAllOption("remote-cert-tls", 1, 1);
 		if(remotetls!=null)
 			if(remotetls.get(0).get(1).equals("server"))
 				np.mExpectTLSCert=true;
@@ -632,14 +604,55 @@ public class ConfigParser {
 				np.mAuthenticationType=VpnProfile.TYPE_USERPASS_KEYSTORE;
 			}
 			if(authuser.size()>1) {
-				// Set option value to password get to get cance to embed later.
+				// Set option value to password get to embed later.
 				np.mUsername=null;
-				np.mPassword=authuser.get(1);
-				useEmbbedUserAuth(np,authuser.get(1));
+				useEmbbedUserAuth(np, authuser.get(1));
 			}
 		}
 
-		// Parse OpenVPN Access Server extra
+        Pair<Connection, Connection[]> conns = parseConnectionOptions(null);
+        np.mConnections =conns.second;
+
+        Vector<Vector<String>> connectionBlocks = getAllOption("connection", 1, 1);
+
+        if (np.mConnections.length > 0 && connectionBlocks !=null ) {
+            throw  new ConfigParseError("Using a <connection> block and --remote is not allowed.");
+        }
+
+        if (connectionBlocks!=null) {
+            np.mConnections = new Connection[connectionBlocks.size()];
+
+            int connIndex = 0;
+            for (Vector<String> conn : connectionBlocks) {
+                Pair<Connection, Connection[]> connectionBlockConnection =
+                        parseConnection(conn.get(1), conns.first);
+
+                if (connectionBlockConnection.second.length != 1)
+                    throw new ConfigParseError("A <connection> block must have exactly one remote");
+                np.mConnections[connIndex] = connectionBlockConnection.second[0];
+                connIndex++;
+            }
+        }
+        if(getOption("remote-random", 0, 0) != null)
+            np.mRemoteRandom=true;
+
+        Vector<String> protoforce = getOption("proto-force", 1, 1);
+        if(protoforce!=null) {
+            boolean disableUDP;
+            String protoToDisable = protoforce.get(1);
+            if (protoToDisable.equals("udp"))
+                disableUDP=true;
+            else if (protoToDisable.equals("tcp"))
+                disableUDP=false;
+            else
+                throw new ConfigParseError(String.format("Unknown protocol %s in proto-force", protoToDisable));
+
+            for (Connection conn:np.mConnections)
+                if(conn.mUseUdp==disableUDP)
+                    conn.mEnabled=false;
+        }
+
+        // Parse OpenVPN Access Server extra
 		Vector<String> friendlyname = meta.get("FRIENDLY_NAME");
 		if(friendlyname !=null && friendlyname.size() > 1)
 			np.mName=friendlyname.get(1);
@@ -649,20 +662,95 @@ public class ConfigParser {
 		if(ocusername !=null && ocusername.size() > 1)
 			np.mUsername=ocusername.get(1);
 
-		// Check the other options
-		if(remotes !=null && remotes.size()>1 && extraRemotesAsCustom) {
-			// first is already added
-			remotes.remove(0);
-			np.mCustomConfigOptions += getOptionStrings(remotes);
-			np.mUseCustomConfig=true;
-
-		}
-		checkIgnoreAndInvalidOptions(np);
+        checkIgnoreAndInvalidOptions(np);
 		fixup(np);
 
 		return np;
 	}
 
+    private Pair<Connection, Connection[]> parseConnection(String connection, Connection defaultValues) throws IOException, ConfigParseError {
+       // Parse a connection Block as a new configuration file
+
+
+        ConfigParser connectionParser = new ConfigParser();
+        StringReader reader = new StringReader(connection.substring(VpnProfile.INLINE_TAG.length()));
+        connectionParser.parseConfig(reader);
+
+        Pair<Connection, Connection[]> conn = connectionParser.parseConnectionOptions(defaultValues);
+
+        return conn;
+    }
+
+    private Pair<Connection, Connection[]> parseConnectionOptions(Connection connDefault) throws ConfigParseError {
+        Connection conn;
+        if (connDefault!=null)
+            try {
+                conn = connDefault.clone();
+            } catch (CloneNotSupportedException e) {
+                e.printStackTrace();
+                return null;
+            }
+        else
+            conn = new Connection();
+
+        Vector<String> port = getOption("port", 1,1);
+        if(port!=null){
+            conn.mServerPort = port.get(1);
+        }
+
+        Vector<String> rport = getOption("rport", 1,1);
+        if(rport!=null){
+            conn.mServerPort = rport.get(1);
+        }
+
+        Vector<String> proto = getOption("proto", 1,1);
+        if(proto!=null){
+            conn.mUseUdp=isUdpProto(proto.get(1));
+        }
+
+
+        // Parse remote config
+        Vector<Vector<String>> remotes = getAllOption("remote",1,3);
+
+
+        // Assume that we need custom options if connectionDefault are set
+        if(connDefault!=null) {
+            for (Vector<Vector<String>> option : options.values()) {
+
+                conn.mCustomConfiguration += getOptionStrings(option);
+
+            }
+            if (!TextUtils.isEmpty(conn.mCustomConfiguration))
+                conn.mUseCustomConfig = true;
+        }
+        // Make remotes empty to simplify code
+        if (remotes==null)
+            remotes = new Vector<Vector<String>>();
+
+        Connection[] connections = new Connection[remotes.size()];
+
+
+        int i=0;
+        for (Vector<String> remote: remotes) {
+            try {
+                connections[i] = conn.clone();
+            } catch (CloneNotSupportedException e) {
+                e.printStackTrace();
+            }
+            switch (remote.size()) {
+                case 4:
+                    connections[i].mUseUdp=isUdpProto(remote.get(3));
+                case 3:
+                    connections[i].mServerPort = remote.get(2);
+                case 2:
+                    connections[i].mServerName = remote.get(1);
+            }
+            i++;
+        }
+        return Pair.create(conn, connections);
+
+    }
+
     private void checkRedirectParameters(VpnProfile np, Vector<Vector<String>> defgw) {
         for (Vector<String> redirect: defgw)
             for (int i=1;i<redirect.size();i++){
@@ -673,25 +761,21 @@ public class ConfigParser {
             }
     }
 
-    public void useExtraRemotesAsCustom(boolean b) {
-		this.extraRemotesAsCustom = b;
-	}
-
 	private boolean isUdpProto(String proto) throws ConfigParseError {
 		boolean isudp;
 		if(proto.equals("udp") || proto.equals("udp6"))
 			isudp=true;
 		else if (proto.equals("tcp-client") ||
-				proto.equals("tcp")  || 
+				proto.equals("tcp")  ||
 				proto.equals("tcp6") ||
 				proto.endsWith("tcp6-client"))
 			isudp =false;
-		else 
+		else
 			throw new ConfigParseError("Unsupported option to --proto " + proto);
 		return isudp;
 	}
 
-	static public void useEmbbedUserAuth(VpnProfile np,String inlinedata)
+	static public void useEmbbedUserAuth(VpnProfile np, String inlinedata)
 	{
 		String data = VpnProfile.getEmbeddedContent(inlinedata);
 		String[] parts = data.split("\n");
diff --git a/app/src/main/java/de/blinkt/openvpn/core/Connection.java b/app/src/main/java/de/blinkt/openvpn/core/Connection.java
new file mode 100644
index 00000000..b10664ce
--- /dev/null
+++ b/app/src/main/java/de/blinkt/openvpn/core/Connection.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012-2014 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.core;
+
+import android.text.TextUtils;
+
+import java.io.Serializable;
+
+public class Connection implements Serializable, Cloneable {
+    public String mServerName = "openvpn.blinkt.de";
+    public String mServerPort = "1194";
+    public boolean mUseUdp = true;
+    public String mCustomConfiguration="";
+    public boolean mUseCustomConfig=false;
+    public boolean mEnabled=true;
+
+    private static final long serialVersionUID = 92031902903829089L;
+
+
+    public String getConnectionBlock() {
+        String cfg="";
+
+        // Server Address
+        cfg += "remote ";
+        cfg += mServerName;
+        cfg += " ";
+        cfg += mServerPort;
+        if (mUseUdp)
+            cfg += " udp\n";
+        else
+            cfg += " tcp-client\n";
+
+        if (!TextUtils.isEmpty(mCustomConfiguration) && mUseCustomConfig) {
+            cfg += mCustomConfiguration;
+            cfg += "\n";
+        }
+        return cfg;
+    }
+
+    @Override
+    public Connection clone() throws CloneNotSupportedException {
+        return (Connection) super.clone();
+    }
+
+    public boolean isOnlyRemote() {
+        return TextUtils.isEmpty(mCustomConfiguration) || !mUseCustomConfig;
+    }
+}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
index 0d75ae51..4ccf5472 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
@@ -182,18 +182,14 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
                     screen = connectState.DISCONNECTED;
 
                 if (shouldBeConnected()) {
-                    if (sendusr1) {
-                        if (lastNetwork == -1) {
-                            mManagement.resume();
-                        } else {
-                            mManagement.reconnect();
-                        }
+                    if (lastNetwork == -1) {
+                        mManagement.resume();
                     } else {
                         mManagement.networkChange();
+
                     }
                 }
 
-
                 lastNetwork = newnet;
             }
         } else if (networkInfo == null) {
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java b/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java
index 83e760ca..56a574dc 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java b/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java
new file mode 100644
index 00000000..440458e4
--- /dev/null
+++ b/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2012-2014 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.core;
+
+import android.annotation.TargetApi;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.Build;
+
+/**
+ * Created by arne on 26.11.14.
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class LollipopDeviceStateListener extends ConnectivityManager.NetworkCallback {
+
+    private String mLastConnectedStatus;
+    private String mLastLinkProperties;
+    private String mLastNetworkCapabilities;
+
+    @Override
+    public void onAvailable(Network network) {
+        super.onAvailable(network);
+
+        if (!network.toString().equals(mLastConnectedStatus)) {
+            mLastConnectedStatus = network.toString();
+            VpnStatus.logDebug("Connected to " + mLastConnectedStatus);
+        }
+    }
+
+    @Override
+    public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
+        super.onLinkPropertiesChanged(network, linkProperties);
+
+        if (!linkProperties.toString().equals(mLastLinkProperties)) {
+            mLastLinkProperties = linkProperties.toString();
+            VpnStatus.logDebug(String.format("Linkproperties of %s: %s", network, linkProperties));
+        }
+    }
+
+    @Override
+    public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
+        super.onCapabilitiesChanged(network, networkCapabilities);
+        if (!networkCapabilities.toString().equals(mLastNetworkCapabilities)) {
+            mLastNetworkCapabilities = networkCapabilities.toString();
+            VpnStatus.logDebug(String.format("Network capabilities of %s: %s", network, networkCapabilities));
+        }
+    }
+}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java b/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java
index 6d7ffdf2..f67b7730 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
@@ -9,6 +9,7 @@ import java.security.InvalidKeyException;
 
 public class NativeUtils {
 	public static native byte[] rsasign(byte[] input,int pkey) throws InvalidKeyException;
+    public static native String[] getIfconfig() throws  IllegalArgumentException;
 	static native void jniclose(int fdint);
 
 	static {
diff --git a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
index 35f46513..26354689 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
@@ -21,6 +21,8 @@ import se.leap.bitmaskclient.BuildConfig;
 public class NetworkSpace {
 
 
+
+
     static class ipAddress implements Comparable<ipAddress> {
         private BigInteger netAddress;
         public int networkMask;
@@ -198,6 +200,13 @@ public class NetworkSpace {
         mIpAddresses.add(new ipAddress(cidrIp, include));
     }
 
+    public void addIPSplit(CIDRIP cidrIp, boolean include) {
+        ipAddress newIP = new ipAddress(cidrIp, include);
+        ipAddress[] splitIps = newIP.split();
+        for (ipAddress split: splitIps)
+            mIpAddresses.add(split);
+    }
+
     void addIPv6(Inet6Address address, int mask, boolean included) {
         mIpAddresses.add(new ipAddress(address, mask, included));
     }
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java
index e90c16d1..1f28c77d 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
index d9830955..578d95e7 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
@@ -14,7 +14,9 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
+import android.net.NetworkRequest;
 import android.net.VpnService;
 import android.os.Binder;
 import android.os.Build;
@@ -23,6 +25,7 @@ import android.os.IBinder;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.preference.PreferenceManager;
+import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -81,6 +84,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
     private String mLastTunCfg;
     private String mRemoteGW;
     private final Object mProcessLock = new Object();
+    private LollipopDeviceStateListener mLollipopDeviceStateListener;
 
     // From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
     public static String humanReadableByteCount(long bytes, boolean mbit) {
@@ -266,6 +270,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
         mDeviceStateReceiver = new DeviceStateReceiver(magnagement);
         registerReceiver(mDeviceStateReceiver, filter);
         VpnStatus.addByteCountListener(mDeviceStateReceiver);
+
+        /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+            addLollipopCMListener(); */
     }
 
     synchronized void unregisterDeviceStateReceiver() {
@@ -280,6 +287,10 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
                 iae.printStackTrace();
             }
         mDeviceStateReceiver = null;
+
+        /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+            removeLollipopCMListener();*/
+
     }
 
     public void userPause(boolean shouldBePaused) {
@@ -320,7 +331,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
             mProfile = ProfileManager.getLastConnectedProfile(this, false);
 
             /* Got no profile, just stop */
-            if (mProfile==null) {
+            if (mProfile == null) {
                 Log.d("OpenVPN", "Got no last connected profile on null intent. Stopping");
                 stopSelf(startId);
                 return START_NOT_STICKY;
@@ -431,7 +442,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
     private OpenVPNManagement instantiateOpenVPN3Core() {
         try {
             Class cl = Class.forName("de.blinkt.openvpn.core.OpenVPNThreadv3");
-            return (OpenVPNManagement) cl.getConstructor(OpenVPNService.class,VpnProfile.class).newInstance(this,mProfile);
+            return (OpenVPNManagement) cl.getConstructor(OpenVPNService.class, VpnProfile.class).newInstance(this, mProfile);
         } catch (IllegalArgumentException e) {
             e.printStackTrace();
         } catch (InstantiationException e) {
@@ -474,6 +485,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
         if (mLocalIPv6 != null)
             cfg += mLocalIPv6;
 
+
         cfg += "routes: " + TextUtils.join("|", mRoutes.getNetworks(true)) + TextUtils.join("|", mRoutesv6.getNetworks(true));
         cfg += "excl. routes:" + TextUtils.join("|", mRoutes.getNetworks(false)) + TextUtils.join("|", mRoutesv6.getNetworks(false));
         cfg += "dns: " + TextUtils.join("|", mDnslist);
@@ -490,6 +502,10 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
 
         VpnStatus.logInfo(R.string.last_openvpn_tun_config);
 
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mProfile.mAllowLocalLAN)
+        {
+            allowAllAFFamilies(builder);
+        }
 
         if (mLocalIP == null && mLocalIPv6 == null) {
             VpnStatus.logError(getString(R.string.opentun_no_ipaddr));
@@ -497,6 +513,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
         }
 
         if (mLocalIP != null) {
+            addLocalNetworksToRoutes();
             try {
                 builder.addAddress(mLocalIP.mIp, mLocalIP.len);
             } catch (IllegalArgumentException iae) {
@@ -527,7 +544,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
 
         String release = Build.VERSION.RELEASE;
         if ((Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT && !release.startsWith("4.4.3")
-                &&  !release.startsWith("4.4.4") &&  !release.startsWith("4.4.5") && !release.startsWith("4.4.6"))
+                && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6"))
                 && mMtu < 1280) {
             VpnStatus.logInfo(String.format(Locale.US, "Forcing MTU to 1280 instead of %d to workaround Android Bug #70916", mMtu));
             builder.setMtu(1280);
@@ -560,8 +577,12 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
         VpnStatus.logInfo(R.string.local_ip_info, mLocalIP.mIp, mLocalIP.len, mLocalIPv6, mMtu);
         VpnStatus.logInfo(R.string.dns_server_info, TextUtils.join(", ", mDnslist), mDomain);
         VpnStatus.logInfo(R.string.routes_info_incl, TextUtils.join(", ", mRoutes.getNetworks(true)), TextUtils.join(", ", mRoutesv6.getNetworks(true)));
-        VpnStatus.logInfo(R.string.routes_info_excl, TextUtils.join(", ", mRoutes.getNetworks(false)),TextUtils.join(", ", mRoutesv6.getNetworks(false)));
+        VpnStatus.logInfo(R.string.routes_info_excl, TextUtils.join(", ", mRoutes.getNetworks(false)), TextUtils.join(", ", mRoutesv6.getNetworks(false)));
         VpnStatus.logDebug(R.string.routes_debug, TextUtils.join(", ", positiveIPv4Routes), TextUtils.join(", ", positiveIPv6Routes));
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            setAllowedVpnPackages(builder);
+        }
+
 
         String session = mProfile.mName;
         if (mLocalIP != null && mLocalIPv6 != null)
@@ -601,6 +622,82 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
 
     }
 
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    private void allowAllAFFamilies(Builder builder) {
+        builder.allowFamily(OsConstants.AF_INET);
+        builder.allowFamily(OsConstants.AF_INET6);
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    void removeLollipopCMListener() {
+        ConnectivityManager cm = (ConnectivityManager) getBaseContext().getSystemService(CONNECTIVITY_SERVICE);
+        cm.unregisterNetworkCallback(mLollipopDeviceStateListener);
+        mLollipopDeviceStateListener = null;
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    void addLollipopCMListener() {
+        ConnectivityManager cm = (ConnectivityManager) getBaseContext().getSystemService(CONNECTIVITY_SERVICE);
+        NetworkRequest.Builder nrb = new NetworkRequest.Builder();
+
+        mLollipopDeviceStateListener = new LollipopDeviceStateListener();
+        cm.registerNetworkCallback(nrb.build(), mLollipopDeviceStateListener);
+    }
+
+    private void addLocalNetworksToRoutes() {
+
+        // Add local network interfaces
+        String[] localRoutes = NativeUtils.getIfconfig();
+
+        // The format of mLocalRoutes is kind of broken because I don't really like JNI
+        for (int i = 0; i < localRoutes.length; i += 3) {
+            String intf = localRoutes[i];
+            String ipAddr = localRoutes[i + 1];
+            String netMask = localRoutes[i + 2];
+
+            if (intf == null || intf.equals("lo") ||
+                    intf.startsWith("tun") || intf.startsWith("rmnet"))
+                continue;
+
+            if (ipAddr==null || netMask == null) {
+                VpnStatus.logError("Local routes are broken?! (Report to author) " + TextUtils.join("|", localRoutes));
+                continue;
+            }
+            
+            if (ipAddr.equals(mLocalIP.mIp))
+                continue;
+
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT && !mProfile.mAllowLocalLAN) {
+                mRoutes.addIPSplit(new CIDRIP(ipAddr, netMask), true);
+
+            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && mProfile.mAllowLocalLAN)
+                mRoutes.addIP(new CIDRIP(ipAddr, netMask), false);
+        }
+    }
+
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    private void setAllowedVpnPackages(Builder builder) {
+        for (String pkg : mProfile.mAllowedAppsVpn) {
+            try {
+                if (mProfile.mAllowedAppsVpnAreDisallowed) {
+                    builder.addDisallowedApplication(pkg);
+                } else {
+                    builder.addAllowedApplication(pkg);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                mProfile.mAllowedAppsVpn.remove(pkg);
+                VpnStatus.logInfo(R.string.app_no_longer_exists, pkg);
+            }
+        }
+
+        if (mProfile.mAllowedAppsVpnAreDisallowed) {
+            VpnStatus.logDebug(R.string.disallowed_vpn_apps_info, TextUtils.join(", ", mProfile.mAllowedAppsVpn));
+        } else {
+            VpnStatus.logDebug(R.string.allowed_vpn_apps_info, TextUtils.join(", ", mProfile.mAllowedAppsVpn));
+        }
+    }
+
     public void addDNS(String dns) {
         mDnslist.add(dns);
     }
@@ -611,28 +708,30 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
         }
     }
 
-    /** Route that is always included, used by the v3 core */
-    public void addRoute (CIDRIP route) {
+    /**
+     * Route that is always included, used by the v3 core
+     */
+    public void addRoute(CIDRIP route) {
         mRoutes.addIP(route, true);
     }
 
-    public void addRoute (String dest, String mask, String gateway, String device) {
+    public void addRoute(String dest, String mask, String gateway, String device) {
         CIDRIP route = new CIDRIP(dest, mask);
         boolean include = isAndroidTunDevice(device);
 
-        NetworkSpace.ipAddress gatewayIP = new NetworkSpace.ipAddress(new CIDRIP(gateway, 32),false);
+        NetworkSpace.ipAddress gatewayIP = new NetworkSpace.ipAddress(new CIDRIP(gateway, 32), false);
 
-        if (mLocalIP==null) {
+        if (mLocalIP == null) {
             VpnStatus.logError("Local IP address unset but adding route?! This is broken! Please contact author with log");
             return;
         }
-        NetworkSpace.ipAddress localNet = new NetworkSpace.ipAddress(mLocalIP,true);
+        NetworkSpace.ipAddress localNet = new NetworkSpace.ipAddress(mLocalIP, true);
         if (localNet.containsNet(gatewayIP))
-            include=true;
+            include = true;
 
-        if (gateway!= null &&
+        if (gateway != null &&
                 (gateway.equals("255.255.255.255") || gateway.equals(mRemoteGW)))
-            include=true;
+            include = true;
 
 
         if (route.len == 32 && !mask.equals("255.255.255.255")) {
@@ -664,7 +763,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
     }
 
     private boolean isAndroidTunDevice(String device) {
-        return device!=null &&
+        return device != null &&
                 (device.startsWith("tun") || "(null)".equals(device) || "vpnservice-tun".equals(device));
     }
 
@@ -679,7 +778,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
     public void setLocalIP(String local, String netmask, int mtu, String mode) {
         mLocalIP = new CIDRIP(local, netmask);
         mMtu = mtu;
-        mRemoteGW=null;
+        mRemoteGW = null;
 
         long netMaskAsInt = CIDRIP.getInt(netmask);
 
@@ -687,14 +786,17 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
             // get the netmask as IP
 
             int masklen;
-            if ("net30".equals(mode))
+            long mask;
+            if ("net30".equals(mode)) {
                 masklen = 30;
-            else
+                mask = 0xfffffffc;
+            } else {
                 masklen = 31;
+                mask = 0xfffffffe;
+            }
 
-            int mask = ~( 1 << (32 - (mLocalIP.len +1)));
             // Netmask is Ip address +/-1, assume net30/p2p with small net
-            if ((netMaskAsInt & mask) == (mLocalIP.getInt() & mask )) {
+            if ((netMaskAsInt & mask) == (mLocalIP.getInt() & mask)) {
                 mLocalIP.len = masklen;
             } else {
                 mLocalIP.len = 32;
@@ -702,13 +804,18 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
                     VpnStatus.logWarning(R.string.ip_not_cidr, local, netmask, mode);
             }
         }
-        if (("p2p".equals(mode)  && mLocalIP.len < 32) || ("net30".equals(mode) && mLocalIP.len < 30)) {
+        if (("p2p".equals(mode) && mLocalIP.len < 32) || ("net30".equals(mode) && mLocalIP.len < 30)) {
             VpnStatus.logWarning(R.string.ip_looks_like_subnet, local, netmask, mode);
         }
 
 
+        /* Workaround for Lollipop, it  does not route traffic to the VPNs own network mask */
+        if (mLocalIP.len <= 31 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+            addRoute(mLocalIP);
+
+
         // Configurations are sometimes really broken...
-        mRemoteGW=netmask;
+        mRemoteGW = netmask;
     }
 
     public void setLocalIPv6(String ipv6addr) {
@@ -810,7 +917,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
         } else {
             String release = Build.VERSION.RELEASE;
             if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT && !release.startsWith("4.4.3")
-                    &&  !release.startsWith("4.4.4") &&  !release.startsWith("4.4.5") && !release.startsWith("4.4.6"))
+                    && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6"))
                 // There will be probably no 4.4.4 or 4.4.5 version, so don't waste effort to do parsing here
                 return "OPEN_AFTER_CLOSE";
             else
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
index e36a5b8a..298a6c40 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
index 37094a1b..1c3b3362 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
@@ -157,7 +157,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
 
             }
         } catch (IOException e) {
-            if (!e.getMessage().equals("socket closed"))
+            if (!e.getMessage().equals("socket closed") && !e.getMessage().equals("Connection reset by peer"))
                 VpnStatus.logException(e);
         }
         synchronized (active) {
diff --git a/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java b/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java
index bca0a4ab..a788426a 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;/*
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java
index 2a26152e..1ebc0a57 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java b/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java
index cf953863..6e2abb13 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
index 208aa359..73ed05bc 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
@@ -76,7 +76,6 @@ public class VPNLaunchHelper {
         args.add("--config");
         args.add(c.getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE);
 
-
         return args.toArray(new String[args.size()]);
     }
 
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
index 25558f13..ffc8097d 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
@@ -480,7 +480,11 @@ public class VpnStatus {
 		newLogItem(new LogItem(LogLevel.INFO, message));
 	}
 
-	public static void logInfo(int resourceId, Object... args) {
+    public static void logDebug(String message) {
+        newLogItem(new LogItem(LogLevel.DEBUG, message));
+    }
+
+    public static void logInfo(int resourceId, Object... args) {
 		newLogItem(new LogItem(LogLevel.INFO, resourceId, args));
 	}
 
diff --git a/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java b/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java
index ff383e0f..0786967b 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
index 77fc21e6..199caa63 100644
--- a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
+++ b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.fragments;
diff --git a/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java b/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java
index e25c2859..82378b00 100644
--- a/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java
+++ b/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
  */
 
 package de.blinkt.openvpn.views;
-- 
cgit v1.2.3


From 8d7bedaa40129ae809f3e1261228b00042872f62 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Wed, 31 Dec 2014 16:51:56 +0100
Subject: Don't remove vpn profiles if possible.

---
 .../main/java/se/leap/bitmaskclient/Dashboard.java |  4 +++-
 .../java/se/leap/bitmaskclient/EipFragment.java    | 17 +++++--------
 .../java/se/leap/bitmaskclient/OnBootReceiver.java | 28 ++++++++++++----------
 .../main/java/se/leap/bitmaskclient/eip/EIP.java   | 20 ++++++++++------
 .../java/se/leap/bitmaskclient/eip/Gateway.java    | 25 +++++++------------
 5 files changed, 47 insertions(+), 47 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index 659cd22c..4e6120ab 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -108,7 +108,9 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
         try {
             provider = new Provider(new URL(preferences.getString(Provider.MAIN_URL, "")));
             provider.define(new JSONObject(preferences.getString(Provider.KEY, "")));
-        } catch (MalformedURLException | JSONException e) {
+        } catch (MalformedURLException e) {
+            e.printStackTrace();
+        } catch (JSONException e) {
             e.printStackTrace();
         }
 
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
index 27c70c43..a21e0cd1 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -109,15 +109,10 @@ public class EipFragment extends Fragment implements Observer {
 	super.onSaveInstanceState(outState);
     }
 
-    protected void saveEipStatus() {
-	boolean eip_is_on = false;
-	Log.d(TAG, "saveEipStatus");
-	if(eip_switch.isChecked()) {
-	    eip_is_on = true;
-	}
-
-	if(parent_activity != null)
-	    Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, eip_is_on).commit();
+    protected void saveStatus() {
+	boolean is_on = eip_switch.isChecked();
+	Log.d(TAG, "saveStatus: is_on = " + is_on);
+	Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, is_on).commit();
     }
 
     @OnCheckedChanged(R.id.eipSwitch)
@@ -127,7 +122,7 @@ public class EipFragment extends Fragment implements Observer {
 	else
 	    handleSwitchOff();
 	
-	saveEipStatus();
+	saveStatus();
     }
 
     private void handleSwitchOn() {
@@ -192,7 +187,7 @@ public class EipFragment extends Fragment implements Observer {
 	
 	if(!eip_switch.isChecked()) {
 	    eip_switch.setChecked(true);
-	    saveEipStatus();
+	    saveStatus();
 	}
 	eipCommand(Constants.ACTION_START_EIP);
     }
diff --git a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
index 07ed6c8f..3b1033bc 100644
--- a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
@@ -3,22 +3,26 @@ package se.leap.bitmaskclient;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 
 import se.leap.bitmaskclient.eip.Constants;
 
 public class OnBootReceiver extends BroadcastReceiver {
 
-	// Debug: am broadcast -a android.intent.action.BOOT_COMPLETED
-	@Override
-	public void onReceive(Context context, Intent intent) {
-	    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(Constants.ACTION_START_EIP);
-		    dashboard_intent.putExtra(Dashboard.ON_BOOT, true);
-		    dashboard_intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-		    context.startActivity(dashboard_intent);
-		}
-	    }
+    SharedPreferences preferences;
+    
+    // Debug: am broadcast -a android.intent.action.BOOT_COMPLETED
+    @Override
+    public void onReceive(Context context, Intent intent) {
+	preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE);
+	boolean provider_configured = !preferences.getString(Provider.KEY, "").isEmpty();
+	boolean start_on_boot = preferences.getBoolean(Dashboard.START_ON_BOOT, false);
+	if(provider_configured && start_on_boot) {
+	    Intent dashboard_intent = new Intent(context, Dashboard.class);
+	    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/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index 3d3070c8..77d0cd82 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -101,7 +101,7 @@ public final class EIP extends IntentService {
 	    stopEIP();
 	else if (action.equals(ACTION_IS_EIP_RUNNING))
 	    isRunning();
-	else if (action.equals(ACTION_UPDATE_EIP_SERVICE))
+        else if (action.equals(ACTION_UPDATE_EIP_SERVICE))
 	    updateEIPService();
 	else if (action.equals(ACTION_CHECK_CERT_VALIDITY))
 	    checkCertValidity();
@@ -174,8 +174,8 @@ public final class EIP extends IntentService {
      */
     private void updateEIPService() {
 	refreshEipDefinition();
-	deleteAllVpnProfiles();
-	updateGateways();
+        if(eip_definition != null)
+            updateGateways();
 	tellToReceiver(ACTION_UPDATE_EIP_SERVICE, Activity.RESULT_OK);
     }
 
@@ -204,15 +204,16 @@ public final class EIP extends IntentService {
      */
     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));
+                    Gateway gateway = new Gateway(eip_definition, context, gw);
+                    if(!gateways.contains(gateway)) {
+                        addGateway(gateway);
+                    }
                 }
             }
-        }
 	} catch (JSONException e) {
 	    // TODO Auto-generated catch block
 	    e.printStackTrace();
@@ -229,8 +230,13 @@ public final class EIP extends IntentService {
     }
 
     private void addGateway(Gateway gateway) {
-	profile_manager.addProfile(gateway.getProfile());
+        VpnProfile profile = gateway.getProfile();
+	profile_manager.addProfile(profile);
+        profile_manager.saveProfile(context, profile);
+        profile_manager.saveProfileList(context);
+
 	gateways.add(gateway);
+        Log.d(TAG, "Gateway added: " + gateway.getProfile().getUUIDString());
     }
 
     private void checkCertValidity() {
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
index 3ee9443c..79239308 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -68,24 +68,8 @@ public class Gateway {
 	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) {
@@ -153,4 +137,13 @@ public class Gateway {
     public int getTimezone() {
 	return timezone;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if(o instanceof Gateway) {
+            return ((Gateway) o).getProfile().mConnections.equals(mVpnProfile.mConnections);
+        }
+        else
+            return super.equals(o);
+    }
 }
-- 
cgit v1.2.3


From 56e05aecf8430ddad776776e536583686555443a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Wed, 31 Dec 2014 17:11:58 +0100
Subject: Update gateway also if credentials changed.

---
 app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
index 79239308..e23f67c7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -141,7 +141,10 @@ public class Gateway {
     @Override
     public boolean equals(Object o) {
         if(o instanceof Gateway) {
-            return ((Gateway) o).getProfile().mConnections.equals(mVpnProfile.mConnections);
+            VpnProfile compared_profile = ((Gateway) o).getProfile();
+            return compared_profile.mConnections.equals(mVpnProfile.mConnections)
+                    && compared_profile.mClientCertFilename != mVpnProfile.mClientCertFilename
+                    && compared_profile.mClientKeyFilename != mVpnProfile.mClientKeyFilename;
         }
         else
             return super.equals(o);
-- 
cgit v1.2.3


From d23ec1613855f6582b0e79c1a9fc1538d6099944 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Wed, 31 Dec 2014 20:50:51 +0100
Subject: Remove duplicated gateways when necessary.

---
 .../main/java/se/leap/bitmaskclient/eip/EIP.java   | 71 +++++++++++++++++++---
 1 file changed, 64 insertions(+), 7 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index 77d0cd82..cf6006e5 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -31,10 +31,13 @@ import org.json.JSONObject;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
+import java.util.NoSuchElementException;
 
 import de.blinkt.openvpn.LaunchVPN;
 import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.Connection;
 import de.blinkt.openvpn.core.ProfileManager;
 import se.leap.bitmaskclient.Dashboard;
 import se.leap.bitmaskclient.EipFragment;
@@ -71,7 +74,7 @@ public final class EIP extends IntentService {
     private static SharedPreferences preferences;
 	
     private static JSONObject eip_definition;
-    private static List<Gateway> gateways = new ArrayList<Gateway>();
+    private static List<Gateway> gateways = new ArrayList<>();
     private static ProfileManager profile_manager;
     private static Gateway gateway;
     
@@ -190,12 +193,6 @@ public final class EIP extends IntentService {
 	    e.printStackTrace();
 	}
     }
-    
-    private void deleteAllVpnProfiles() {
-	Collection<VpnProfile> profiles = profile_manager.getProfiles();
-	profiles.removeAll(profiles);
-	gateways.clear();
-    }
 	
     /**
      * Walk the list of gateways defined in eip-service.json and parse them into
@@ -231,6 +228,8 @@ public final class EIP extends IntentService {
 
     private void addGateway(Gateway gateway) {
         VpnProfile profile = gateway.getProfile();
+        removeGateway(gateway);
+
 	profile_manager.addProfile(profile);
         profile_manager.saveProfile(context, profile);
         profile_manager.saveProfileList(context);
@@ -239,6 +238,64 @@ public final class EIP extends IntentService {
         Log.d(TAG, "Gateway added: " + gateway.getProfile().getUUIDString());
     }
 
+    private void removeGateway(Gateway gateway) {
+        VpnProfile profile = gateway.getProfile();
+        removeDuplicatedProfile(profile);
+        removeDuplicatedGateway(profile);
+    }
+
+    private void removeDuplicatedProfile(VpnProfile remove) {
+        if(containsProfile(remove))
+            profile_manager.removeProfile(context, duplicatedProfile(remove));
+        if(containsProfile(remove)) removeDuplicatedProfile(remove);
+    }
+
+    private boolean containsProfile(VpnProfile profile) {
+        Collection<VpnProfile> profiles = profile_manager.getProfiles();
+        for(VpnProfile aux : profiles) {
+            if (sameConnections(profile.mConnections, aux.mConnections)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private VpnProfile duplicatedProfile(VpnProfile profile) {
+        VpnProfile duplicated = null;
+        Collection<VpnProfile> profiles = profile_manager.getProfiles();
+        for(VpnProfile aux : profiles) {
+            if (sameConnections(profile.mConnections, aux.mConnections)) {
+                duplicated = aux;
+            }
+        }
+        if(duplicated != null) return duplicated;
+        else throw new NoSuchElementException(profile.getName());
+    }
+
+    private boolean sameConnections(Connection[] c1, Connection[] c2) {
+        int same_connections = 0;
+        for(Connection c1_aux : c1) {
+            for(Connection c2_aux : c2)
+                if(c2_aux.mServerName.equals(c1_aux.mServerName)) {
+                    same_connections++;
+                    break;
+                }
+        }
+        return c1.length == c2.length && c1.length == same_connections;
+
+    }
+
+    private void removeDuplicatedGateway(VpnProfile profile) {
+        Iterator<Gateway> it = gateways.iterator();
+        List<Gateway> gateways_to_remove = new ArrayList<>();
+        while(it.hasNext()) {
+            Gateway aux = it.next();
+            if(aux.getProfile().mConnections == profile.mConnections)
+                gateways_to_remove.add(aux);
+        }
+        gateways.removeAll(gateways_to_remove);
+    }
+
     private void checkCertValidity() {
 	VpnCertificateValidator validator = new VpnCertificateValidator();
 	int resultCode = validator.isValid(preferences.getString(CERTIFICATE, "")) ?
-- 
cgit v1.2.3


From 407e3767744e39a59845423246a9ad427d933304 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Fri, 2 Jan 2015 01:32:22 +0100
Subject: Update vpn profiles correctly.

Before we add a new profile, we check if there are any duplicated ones
with the same server IPs and ports. If they've the same credentials of
the new one, we don't add anything; if not, we remove the old ones and
add the new.
---
 .../main/java/se/leap/bitmaskclient/eip/EIP.java   | 22 ++++++++++++++++++----
 .../java/se/leap/bitmaskclient/eip/Gateway.java    | 15 ++-------------
 2 files changed, 20 insertions(+), 17 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index cf6006e5..10d222c2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -125,8 +125,9 @@ public final class EIP extends IntentService {
 	if(gateway != null && gateway.getProfile() != null) {
 	    mReceiver = EipFragment.getReceiver();
 	    launchActiveGateway();
-	}
-	tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK);
+        tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK);
+	} else
+        tellToReceiver(ACTION_START_EIP, Activity.RESULT_CANCELED);
     }
 
     /**
@@ -144,6 +145,8 @@ public final class EIP extends IntentService {
 	intent.setAction(Intent.ACTION_MAIN);
 	intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 	intent.putExtra(LaunchVPN.EXTRA_NAME, gateway.getProfile().getName());
+        Log.d(TAG, gateway.getProfile().mClientCertFilename);
+        Log.d(TAG, gateway.getProfile().mClientKeyFilename);
 	intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
 	startActivity(intent);
     }
@@ -206,7 +209,7 @@ public final class EIP extends IntentService {
                 JSONObject gw = gatewaysDefined.getJSONObject(i);
                 if (isOpenVpnGateway(gw)) {
                     Gateway gateway = new Gateway(eip_definition, context, gw);
-                    if(!gateways.contains(gateway)) {
+                    if(!containsProfileWithSecrets(gateway.getProfile())) {
                         addGateway(gateway);
                     }
                 }
@@ -260,6 +263,17 @@ public final class EIP extends IntentService {
         return false;
     }
 
+    private boolean containsProfileWithSecrets(VpnProfile profile) {
+        if(!containsProfile(profile)) return false;
+
+        Collection<VpnProfile> profiles = profile_manager.getProfiles();
+        for(VpnProfile aux : profiles) {
+            return profile.mClientCertFilename.equalsIgnoreCase(aux.mClientCertFilename)
+                    && profile.mClientKeyFilename.equalsIgnoreCase(aux.mClientKeyFilename);
+        }
+
+        return false;
+    }
     private VpnProfile duplicatedProfile(VpnProfile profile) {
         VpnProfile duplicated = null;
         Collection<VpnProfile> profiles = profile_manager.getProfiles();
@@ -290,7 +304,7 @@ public final class EIP extends IntentService {
         List<Gateway> gateways_to_remove = new ArrayList<>();
         while(it.hasNext()) {
             Gateway aux = it.next();
-            if(aux.getProfile().mConnections == profile.mConnections)
+            if(sameConnections(aux.getProfile().mConnections, profile.mConnections))
                 gateways_to_remove.add(aux);
         }
         gateways.removeAll(gateways_to_remove);
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
index e23f67c7..005d0eec 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -25,6 +25,7 @@ import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.io.IOException;
+import java.io.Serializable;
 import java.io.StringReader;
 import java.util.Collection;
 import java.util.Iterator;
@@ -42,7 +43,7 @@ import se.leap.bitmaskclient.Dashboard;
  * @author Sean Leonard <meanderingcode@aetherislands.net>
  * @author Parménides GV <parmegv@sdf.org>
  */
-public class Gateway {
+public class Gateway implements Serializable {
 		
     private String TAG = Gateway.class.getSimpleName();
 		
@@ -137,16 +138,4 @@ public class Gateway {
     public int getTimezone() {
 	return timezone;
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if(o instanceof Gateway) {
-            VpnProfile compared_profile = ((Gateway) o).getProfile();
-            return compared_profile.mConnections.equals(mVpnProfile.mConnections)
-                    && compared_profile.mClientCertFilename != mVpnProfile.mClientCertFilename
-                    && compared_profile.mClientKeyFilename != mVpnProfile.mClientKeyFilename;
-        }
-        else
-            return super.equals(o);
-    }
 }
-- 
cgit v1.2.3


From 38fdfbdacf414ab85fffd833f57e5fc93b03ad4f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Fri, 2 Jan 2015 13:45:31 +0100
Subject: Serialized gateways and correctly updating profiles

---
 .../java/se/leap/bitmaskclient/EipFragment.java    |   2 +-
 .../java/se/leap/bitmaskclient/OnBootReceiver.java |   3 +
 .../main/java/se/leap/bitmaskclient/eip/EIP.java   | 104 ++++++++++++++++-----
 .../java/se/leap/bitmaskclient/eip/Gateway.java    |  34 +++----
 .../leap/bitmaskclient/eip/VpnConfigGenerator.java |  56 +++++------
 5 files changed, 126 insertions(+), 73 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
index a21e0cd1..54432033 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -187,8 +187,8 @@ public class EipFragment extends Fragment implements Observer {
 	
 	if(!eip_switch.isChecked()) {
 	    eip_switch.setChecked(true);
-	    saveStatus();
 	}
+        saveStatus();
 	eipCommand(Constants.ACTION_START_EIP);
     }
 
diff --git a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
index 3b1033bc..96b87085 100644
--- a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
@@ -4,6 +4,7 @@ import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.util.Log;
 
 import se.leap.bitmaskclient.eip.Constants;
 
@@ -17,6 +18,8 @@ public class OnBootReceiver extends BroadcastReceiver {
 	preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE);
 	boolean provider_configured = !preferences.getString(Provider.KEY, "").isEmpty();
 	boolean start_on_boot = preferences.getBoolean(Dashboard.START_ON_BOOT, false);
+        Log.d("OnBootReceiver", "Provider configured " + String.valueOf(provider_configured));
+        Log.d("OnBootReceiver", "Start on boot " + String.valueOf(start_on_boot));
 	if(provider_configured && start_on_boot) {
 	    Intent dashboard_intent = new Intent(context, Dashboard.class);
 	    dashboard_intent.setAction(Constants.ACTION_START_EIP);
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index 10d222c2..dcf36a82 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -25,10 +25,14 @@ import android.os.Bundle;
 import android.os.ResultReceiver;
 import android.util.Log;
 
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -41,6 +45,7 @@ import de.blinkt.openvpn.core.Connection;
 import de.blinkt.openvpn.core.ProfileManager;
 import se.leap.bitmaskclient.Dashboard;
 import se.leap.bitmaskclient.EipFragment;
+import se.leap.bitmaskclient.Provider;
 
 import static se.leap.bitmaskclient.eip.Constants.ACTION_CHECK_CERT_VALIDITY;
 import static se.leap.bitmaskclient.eip.Constants.ACTION_IS_EIP_RUNNING;
@@ -87,12 +92,14 @@ public final class EIP extends IntentService {
 		super.onCreate();
 		
 		context = getApplicationContext();
-		profile_manager = ProfileManager.getInstance(context);
+        preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
 
-		preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
-		refreshEipDefinition();
+		profile_manager = ProfileManager.getInstance(context);
+		eip_definition = eipDefinitionFromPreferences();
+        if(gateways.isEmpty())
+            gateways = gatewaysFromPreferences();
 	}
-	
+
     @Override
     protected void onHandleIntent(Intent intent) {
 	String action = intent.getAction();
@@ -122,6 +129,7 @@ public final class EIP extends IntentService {
 
         GatewaySelector gateway_selector = new GatewaySelector(gateways);
 	gateway = gateway_selector.select();
+	Log.d(TAG, "Connecting to " + gateway.getProfile().getUUIDString());
 	if(gateway != null && gateway.getProfile() != null) {
 	    mReceiver = EipFragment.getReceiver();
 	    launchActiveGateway();
@@ -179,28 +187,42 @@ public final class EIP extends IntentService {
      * TODO Implement API call to refresh eip-service.json from the provider
      */
     private void updateEIPService() {
-	refreshEipDefinition();
+	eip_definition = eipDefinitionFromPreferences();
         if(eip_definition != null)
             updateGateways();
 	tellToReceiver(ACTION_UPDATE_EIP_SERVICE, Activity.RESULT_OK);
     }
 
-    private void refreshEipDefinition() {
+    private JSONObject eipDefinitionFromPreferences() {
 	try {
 	    String eip_definition_string = preferences.getString(KEY, "");
 	    if(!eip_definition_string.isEmpty()) {
-		eip_definition = new JSONObject(eip_definition_string);
+		return new JSONObject(eip_definition_string);
 	    }
 	} catch (JSONException e) {
 	    // TODO Auto-generated catch block
 	    e.printStackTrace();
 	}
+        return null;
+    }
+
+    private List<Gateway> gatewaysFromPreferences() {
+        List<Gateway> result;
+
+        String gateways_string = preferences.getString(Gateway.TAG, "");
+        Log.d(TAG, "Recovering gateways: " + gateways_string);
+        Type type_list_gateways = new TypeToken<ArrayList<Gateway>>() {}.getType();
+        result = gateways_string.isEmpty() ?
+                new ArrayList<Gateway>()
+                : (List<Gateway>) new Gson().fromJson(gateways_string, type_list_gateways);
+	Log.d(TAG, "Gateways from preferences = " + result.size());
+        preferences.edit().remove(Gateway.TAG);
+        return result;
     }
 	
     /**
      * 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 {
@@ -208,12 +230,15 @@ public final class EIP extends IntentService {
             for (int i = 0; i < gatewaysDefined.length(); i++) {
                 JSONObject gw = gatewaysDefined.getJSONObject(i);
                 if (isOpenVpnGateway(gw)) {
-                    Gateway gateway = new Gateway(eip_definition, context, gw);
-                    if(!containsProfileWithSecrets(gateway.getProfile())) {
-                        addGateway(gateway);
+                    JSONObject secrets = secretsConfiguration();
+                    Gateway aux = new Gateway(eip_definition, secrets, gw);
+		    Log.d(TAG, "Possible new gateway: " + aux.getProfile().getUUIDString());
+                    if(!containsProfileWithSecrets(aux.getProfile())) {
+                        addGateway(aux);
                     }
                 }
             }
+	    gatewaysToPreferences();
 	} catch (JSONException e) {
 	    // TODO Auto-generated catch block
 	    e.printStackTrace();
@@ -229,6 +254,19 @@ public final class EIP extends IntentService {
 	}
     }
 
+
+    private JSONObject secretsConfiguration() {
+        JSONObject result = new JSONObject();
+        try {
+            result.put(Provider.CA_CERT, preferences.getString(Provider.CA_CERT, ""));
+            result.put(Constants.PRIVATE_KEY, preferences.getString(Constants.PRIVATE_KEY, ""));
+            result.put(Constants.CERTIFICATE, preferences.getString(Constants.CERTIFICATE, ""));
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+
     private void addGateway(Gateway gateway) {
         VpnProfile profile = gateway.getProfile();
         removeGateway(gateway);
@@ -247,10 +285,12 @@ public final class EIP extends IntentService {
         removeDuplicatedGateway(profile);
     }
 
-    private void removeDuplicatedProfile(VpnProfile remove) {
-        if(containsProfile(remove))
-            profile_manager.removeProfile(context, duplicatedProfile(remove));
-        if(containsProfile(remove)) removeDuplicatedProfile(remove);
+    private void removeDuplicatedProfile(VpnProfile original) {
+        if(containsProfile(original)) {
+            VpnProfile remove = duplicatedProfile(original);
+            profile_manager.removeProfile(context, remove);
+	    Log.d(TAG, "Removing profile " + remove.getUUIDString());
+	}if(containsProfile(original)) removeDuplicatedProfile(original);
     }
 
     private boolean containsProfile(VpnProfile profile) {
@@ -264,16 +304,21 @@ public final class EIP extends IntentService {
     }
 
     private boolean containsProfileWithSecrets(VpnProfile profile) {
-        if(!containsProfile(profile)) return false;
-
-        Collection<VpnProfile> profiles = profile_manager.getProfiles();
-        for(VpnProfile aux : profiles) {
-            return profile.mClientCertFilename.equalsIgnoreCase(aux.mClientCertFilename)
-                    && profile.mClientKeyFilename.equalsIgnoreCase(aux.mClientKeyFilename);
-        }
-
-        return false;
+	boolean result = false;
+	
+        if(containsProfile(profile)) {
+	    Collection<VpnProfile> profiles = profile_manager.getProfiles();
+	    for(VpnProfile aux : profiles) {
+		result = result == false ?
+		    sameConnections(profile.mConnections, aux.mConnections)
+                    && profile.mClientCertFilename.equalsIgnoreCase(aux.mClientCertFilename)
+                    && profile.mClientKeyFilename.equalsIgnoreCase(aux.mClientKeyFilename)
+		    : true;
+	    }
+	}
+        return result;
     }
+    
     private VpnProfile duplicatedProfile(VpnProfile profile) {
         VpnProfile duplicated = null;
         Collection<VpnProfile> profiles = profile_manager.getProfiles();
@@ -304,12 +349,21 @@ public final class EIP extends IntentService {
         List<Gateway> gateways_to_remove = new ArrayList<>();
         while(it.hasNext()) {
             Gateway aux = it.next();
-            if(sameConnections(aux.getProfile().mConnections, profile.mConnections))
+            if(sameConnections(aux.getProfile().mConnections, profile.mConnections)) {
                 gateways_to_remove.add(aux);
+		Log.d(TAG, "Removing gateway " + aux.getProfile().getUUIDString());
+	    }
         }
         gateways.removeAll(gateways_to_remove);
     }
 
+    private void gatewaysToPreferences() {
+        Type type_list_gateways = new TypeToken<List<Gateway>>() {}.getType();
+        String gateways_string = new Gson().toJson(gateways, type_list_gateways);
+        Log.d(TAG, "Saving gateways: " + gateways_string);
+        preferences.edit().putString(Gateway.TAG, gateways_string).apply();
+    }
+
     private void checkCertValidity() {
 	VpnCertificateValidator validator = new VpnCertificateValidator();
 	int resultCode = validator.isValid(preferences.getString(CERTIFICATE, "")) ?
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
index 005d0eec..daf7d4a7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -17,7 +17,6 @@
 package se.leap.bitmaskclient.eip;
 
 import android.app.Activity;
-import android.content.Context;
 import android.content.SharedPreferences;
 import android.util.Log;
 
@@ -25,14 +24,10 @@ import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.io.IOException;
-import java.io.Serializable;
 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;
 
 /**
@@ -43,28 +38,26 @@ import se.leap.bitmaskclient.Dashboard;
  * @author Sean Leonard <meanderingcode@aetherislands.net>
  * @author Parménides GV <parmegv@sdf.org>
  */
-public class Gateway implements Serializable {
-		
-    private String TAG = Gateway.class.getSimpleName();
+public class Gateway {
 		
+    public static String TAG = Gateway.class.getSimpleName();
+
+    private JSONObject general_configuration;
+    private JSONObject secrets;
+    private JSONObject gateway;
+
     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){
+    protected Gateway(JSONObject eip_definition, JSONObject secrets, JSONObject gateway){
+
+	this.gateway = gateway;
+        this.secrets = secrets;
 
-	mGateway = gateway;
-	
-	this.context = context;
 	general_configuration = getGeneralConfiguration(eip_definition);
 	timezone = getTimezone(eip_definition);
 	mName = locationAsName(eip_definition);
@@ -95,7 +88,7 @@ public class Gateway implements Serializable {
 	try {
 	    JSONObject locations = eip_definition.getJSONObject("locations");
 
-	    return locations.getJSONObject(mGateway.getString("location"));
+	    return locations.getJSONObject(gateway.getString("location"));
 	} catch (JSONException e) {
 	    return new JSONObject();
 	}
@@ -108,8 +101,7 @@ public class Gateway implements Serializable {
 	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);
+	    VpnConfigGenerator vpn_configuration_generator = new VpnConfigGenerator(general_configuration, secrets, gateway);
 	    String configuration = vpn_configuration_generator.generate();
 				
 	    cp.parseConfig(new StringReader(configuration));
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
index a320bee5..6f260f55 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
@@ -31,15 +31,15 @@ public class VpnConfigGenerator {
 
     private JSONObject general_configuration;
     private JSONObject gateway;
-    
-    private static SharedPreferences preferences;
+    private JSONObject secrets;
+
     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) {
+    public VpnConfigGenerator(JSONObject general_configuration, JSONObject secrets, JSONObject gateway) {
 	this.general_configuration = general_configuration;
 	this.gateway = gateway;
-	VpnConfigGenerator.preferences = preferences;
+        this.secrets = secrets;
     }
     
     public String generate() {
@@ -113,29 +113,33 @@ public class VpnConfigGenerator {
     }
 
     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>";
+        try {
+            String ca =
+                    "<ca>"
+                            + new_line
+                            + secrets.getString(Provider.CA_CERT)
+                            + new_line
+                            + "</ca>";
+
+            String key =
+                    "<key>"
+                            + new_line
+                            + secrets.getString(Constants.PRIVATE_KEY)
+                            + new_line
+                            + "</key>";
+
+            String openvpn_cert =
+                    "<cert>"
+                            + new_line
+                            + secrets.getString(Constants.CERTIFICATE)
+                            + new_line
+                            + "</cert>";
 
-	return ca + new_line + key + new_line + openvpn_cert;
+            return ca + new_line + key + new_line + openvpn_cert;
+        } catch(JSONException e) {
+            e.printStackTrace();
+            return "";
+        }
     }
 
     private String androidCustomizations() {
-- 
cgit v1.2.3


From 7ef5c878e754548ab6a854916799290b043ac8d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Fri, 2 Jan 2015 13:55:14 +0100
Subject: Don't print certificates on logcat!

---
 app/src/main/java/se/leap/bitmaskclient/eip/EIP.java | 11 -----------
 1 file changed, 11 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index dcf36a82..533b0281 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -129,7 +129,6 @@ public final class EIP extends IntentService {
 
         GatewaySelector gateway_selector = new GatewaySelector(gateways);
 	gateway = gateway_selector.select();
-	Log.d(TAG, "Connecting to " + gateway.getProfile().getUUIDString());
 	if(gateway != null && gateway.getProfile() != null) {
 	    mReceiver = EipFragment.getReceiver();
 	    launchActiveGateway();
@@ -153,15 +152,12 @@ public final class EIP extends IntentService {
 	intent.setAction(Intent.ACTION_MAIN);
 	intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 	intent.putExtra(LaunchVPN.EXTRA_NAME, gateway.getProfile().getName());
-        Log.d(TAG, gateway.getProfile().mClientCertFilename);
-        Log.d(TAG, gateway.getProfile().mClientKeyFilename);
 	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() || eip_status.isConnecting())
 	    result_code = Activity.RESULT_OK;
@@ -210,12 +206,10 @@ public final class EIP extends IntentService {
         List<Gateway> result;
 
         String gateways_string = preferences.getString(Gateway.TAG, "");
-        Log.d(TAG, "Recovering gateways: " + gateways_string);
         Type type_list_gateways = new TypeToken<ArrayList<Gateway>>() {}.getType();
         result = gateways_string.isEmpty() ?
                 new ArrayList<Gateway>()
                 : (List<Gateway>) new Gson().fromJson(gateways_string, type_list_gateways);
-	Log.d(TAG, "Gateways from preferences = " + result.size());
         preferences.edit().remove(Gateway.TAG);
         return result;
     }
@@ -232,7 +226,6 @@ public final class EIP extends IntentService {
                 if (isOpenVpnGateway(gw)) {
                     JSONObject secrets = secretsConfiguration();
                     Gateway aux = new Gateway(eip_definition, secrets, gw);
-		    Log.d(TAG, "Possible new gateway: " + aux.getProfile().getUUIDString());
                     if(!containsProfileWithSecrets(aux.getProfile())) {
                         addGateway(aux);
                     }
@@ -276,7 +269,6 @@ public final class EIP extends IntentService {
         profile_manager.saveProfileList(context);
 
 	gateways.add(gateway);
-        Log.d(TAG, "Gateway added: " + gateway.getProfile().getUUIDString());
     }
 
     private void removeGateway(Gateway gateway) {
@@ -289,7 +281,6 @@ public final class EIP extends IntentService {
         if(containsProfile(original)) {
             VpnProfile remove = duplicatedProfile(original);
             profile_manager.removeProfile(context, remove);
-	    Log.d(TAG, "Removing profile " + remove.getUUIDString());
 	}if(containsProfile(original)) removeDuplicatedProfile(original);
     }
 
@@ -351,7 +342,6 @@ public final class EIP extends IntentService {
             Gateway aux = it.next();
             if(sameConnections(aux.getProfile().mConnections, profile.mConnections)) {
                 gateways_to_remove.add(aux);
-		Log.d(TAG, "Removing gateway " + aux.getProfile().getUUIDString());
 	    }
         }
         gateways.removeAll(gateways_to_remove);
@@ -360,7 +350,6 @@ public final class EIP extends IntentService {
     private void gatewaysToPreferences() {
         Type type_list_gateways = new TypeToken<List<Gateway>>() {}.getType();
         String gateways_string = new Gson().toJson(gateways, type_list_gateways);
-        Log.d(TAG, "Saving gateways: " + gateways_string);
         preferences.edit().putString(Gateway.TAG, gateways_string).apply();
     }
 
-- 
cgit v1.2.3


From dd6e5a29f40f38481dec5545ff4fe1cabd5d6384 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Fri, 12 Dec 2014 10:43:54 +0100
Subject: Beginning the tests for EIP.

---
 app/src/main/java/se/leap/bitmaskclient/eip/EIP.java              | 4 ++--
 .../java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java   | 8 +++++++-
 2 files changed, 9 insertions(+), 3 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index 533b0281..a86b9c6a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -354,8 +354,8 @@ public final class EIP extends IntentService {
     }
 
     private void checkCertValidity() {
-	VpnCertificateValidator validator = new VpnCertificateValidator();
-	int resultCode = validator.isValid(preferences.getString(CERTIFICATE, "")) ?
+	VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(CERTIFICATE, ""));
+	int resultCode = validator.isValid() ?
 	    Activity.RESULT_OK :
 	    Activity.RESULT_CANCELED;
 	tellToReceiver(ACTION_CHECK_CERT_VALIDITY, resultCode);
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
index 6487f6c1..0bbe9db4 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
@@ -28,7 +28,13 @@ import se.leap.bitmaskclient.ConfigHelper;
 public class VpnCertificateValidator {
     public final static String TAG = VpnCertificateValidator.class.getSimpleName();
 
-    public boolean isValid(String certificate) {
+    private String certificate;
+
+    public VpnCertificateValidator(String certificate) {
+        this.certificate = certificate;
+    }
+
+    public boolean isValid() {
 	if(!certificate.isEmpty()) {
 	    X509Certificate certificate_x509 = ConfigHelper.parseX509CertificateFromString(certificate);
 	    return isValid(certificate_x509);
-- 
cgit v1.2.3


From a74d09929575e44f86d09283ae4633b0dcfcb566 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Tue, 6 Jan 2015 10:11:50 +0100
Subject: Extracted GatewaysManager + coded its tests

---
 .../main/java/se/leap/bitmaskclient/eip/EIP.java   | 239 ++++-----------------
 .../java/se/leap/bitmaskclient/eip/Gateway.java    |  11 +-
 .../se/leap/bitmaskclient/eip/GatewaysManager.java | 184 ++++++++++++++++
 3 files changed, 230 insertions(+), 204 deletions(-)
 create mode 100644 app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index a86b9c6a..a5cc817a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -16,46 +16,17 @@
  */
 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.ResultReceiver;
+import android.app.*;
+import android.content.*;
+import android.os.*;
 import android.util.Log;
 
-import com.google.gson.Gson;
-import com.google.gson.reflect.TypeToken;
+import org.json.*;
 
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
+import de.blinkt.openvpn.*;
+import se.leap.bitmaskclient.*;
 
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-import de.blinkt.openvpn.LaunchVPN;
-import de.blinkt.openvpn.VpnProfile;
-import de.blinkt.openvpn.core.Connection;
-import de.blinkt.openvpn.core.ProfileManager;
-import se.leap.bitmaskclient.Dashboard;
-import se.leap.bitmaskclient.EipFragment;
-import se.leap.bitmaskclient.Provider;
-
-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;
+import static se.leap.bitmaskclient.eip.Constants.*;
 
 /**
  * EIP is the abstract base class for interacting with and managing the Encrypted
@@ -79,26 +50,25 @@ public final class EIP extends IntentService {
     private static SharedPreferences preferences;
 	
     private static JSONObject eip_definition;
-    private static List<Gateway> gateways = new ArrayList<>();
-    private static ProfileManager profile_manager;
+    private static GatewaysManager gateways_manager = new GatewaysManager();
     private static Gateway gateway;
     
-	public EIP(){
-		super(TAG);
-	}
+    public EIP(){
+	super(TAG);
+    }
 	
-	@Override
-	public void onCreate() {
-		super.onCreate();
+    @Override
+    public void onCreate() {
+	super.onCreate();
 		
-		context = getApplicationContext();
+	context = getApplicationContext();
         preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
-
-		profile_manager = ProfileManager.getInstance(context);
-		eip_definition = eipDefinitionFromPreferences();
-        if(gateways.isEmpty())
-            gateways = gatewaysFromPreferences();
-	}
+	eip_definition = eipDefinitionFromPreferences();
+        if(gateways_manager.isEmpty()) {
+            gateways_manager = new GatewaysManager(context, preferences);
+            gatewaysFromPreferences();
+        }
+    }
 
     @Override
     protected void onHandleIntent(Intent intent) {
@@ -123,18 +93,17 @@ public final class EIP extends IntentService {
      * It also sets up early routes.
      */
     private void startEIP() {
-	if(gateways.isEmpty())
+	if(gateways_manager.isEmpty())
 	    updateEIPService();
         earlyRoutes();
 
-        GatewaySelector gateway_selector = new GatewaySelector(gateways);
-	gateway = gateway_selector.select();
+	gateway = gateways_manager.select();
 	if(gateway != null && gateway.getProfile() != null) {
 	    mReceiver = EipFragment.getReceiver();
 	    launchActiveGateway();
-        tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK);
+	    tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK);
 	} else
-        tellToReceiver(ACTION_START_EIP, Activity.RESULT_CANCELED);
+	    tellToReceiver(ACTION_START_EIP, Activity.RESULT_CANCELED);
     }
 
     /**
@@ -184,173 +153,39 @@ public final class EIP extends IntentService {
      */
     private void updateEIPService() {
 	eip_definition = eipDefinitionFromPreferences();
-        if(eip_definition != null)
+        if(eip_definition.length() > 0)
             updateGateways();
 	tellToReceiver(ACTION_UPDATE_EIP_SERVICE, Activity.RESULT_OK);
     }
 
     private JSONObject eipDefinitionFromPreferences() {
+        JSONObject result = new JSONObject();
 	try {
 	    String eip_definition_string = preferences.getString(KEY, "");
 	    if(!eip_definition_string.isEmpty()) {
-		return new JSONObject(eip_definition_string);
+		result = new JSONObject(eip_definition_string);
 	    }
 	} catch (JSONException e) {
 	    // TODO Auto-generated catch block
 	    e.printStackTrace();
 	}
-        return null;
-    }
-
-    private List<Gateway> gatewaysFromPreferences() {
-        List<Gateway> result;
-
-        String gateways_string = preferences.getString(Gateway.TAG, "");
-        Type type_list_gateways = new TypeToken<ArrayList<Gateway>>() {}.getType();
-        result = gateways_string.isEmpty() ?
-                new ArrayList<Gateway>()
-                : (List<Gateway>) new Gson().fromJson(gateways_string, type_list_gateways);
-        preferences.edit().remove(Gateway.TAG);
-        return result;
-    }
-	
-    /**
-     * Walk the list of gateways defined in eip-service.json and parse them into
-     * Gateway objects.
-     */
-    private void updateGateways(){
-	try {
-            JSONArray gatewaysDefined = eip_definition.getJSONArray("gateways");
-            for (int i = 0; i < gatewaysDefined.length(); i++) {
-                JSONObject gw = gatewaysDefined.getJSONObject(i);
-                if (isOpenVpnGateway(gw)) {
-                    JSONObject secrets = secretsConfiguration();
-                    Gateway aux = new Gateway(eip_definition, secrets, gw);
-                    if(!containsProfileWithSecrets(aux.getProfile())) {
-                        addGateway(aux);
-                    }
-                }
-            }
-	    gatewaysToPreferences();
-	} 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 JSONObject secretsConfiguration() {
-        JSONObject result = new JSONObject();
-        try {
-            result.put(Provider.CA_CERT, preferences.getString(Provider.CA_CERT, ""));
-            result.put(Constants.PRIVATE_KEY, preferences.getString(Constants.PRIVATE_KEY, ""));
-            result.put(Constants.CERTIFICATE, preferences.getString(Constants.CERTIFICATE, ""));
-        } catch (JSONException e) {
-            e.printStackTrace();
-        }
         return result;
     }
 
-    private void addGateway(Gateway gateway) {
-        VpnProfile profile = gateway.getProfile();
-        removeGateway(gateway);
-
-	profile_manager.addProfile(profile);
-        profile_manager.saveProfile(context, profile);
-        profile_manager.saveProfileList(context);
-
-	gateways.add(gateway);
-    }
-
-    private void removeGateway(Gateway gateway) {
-        VpnProfile profile = gateway.getProfile();
-        removeDuplicatedProfile(profile);
-        removeDuplicatedGateway(profile);
-    }
-
-    private void removeDuplicatedProfile(VpnProfile original) {
-        if(containsProfile(original)) {
-            VpnProfile remove = duplicatedProfile(original);
-            profile_manager.removeProfile(context, remove);
-	}if(containsProfile(original)) removeDuplicatedProfile(original);
-    }
-
-    private boolean containsProfile(VpnProfile profile) {
-        Collection<VpnProfile> profiles = profile_manager.getProfiles();
-        for(VpnProfile aux : profiles) {
-            if (sameConnections(profile.mConnections, aux.mConnections)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean containsProfileWithSecrets(VpnProfile profile) {
-	boolean result = false;
-	
-        if(containsProfile(profile)) {
-	    Collection<VpnProfile> profiles = profile_manager.getProfiles();
-	    for(VpnProfile aux : profiles) {
-		result = result == false ?
-		    sameConnections(profile.mConnections, aux.mConnections)
-                    && profile.mClientCertFilename.equalsIgnoreCase(aux.mClientCertFilename)
-                    && profile.mClientKeyFilename.equalsIgnoreCase(aux.mClientKeyFilename)
-		    : true;
-	    }
-	}
-        return result;
-    }
-    
-    private VpnProfile duplicatedProfile(VpnProfile profile) {
-        VpnProfile duplicated = null;
-        Collection<VpnProfile> profiles = profile_manager.getProfiles();
-        for(VpnProfile aux : profiles) {
-            if (sameConnections(profile.mConnections, aux.mConnections)) {
-                duplicated = aux;
-            }
-        }
-        if(duplicated != null) return duplicated;
-        else throw new NoSuchElementException(profile.getName());
-    }
-
-    private boolean sameConnections(Connection[] c1, Connection[] c2) {
-        int same_connections = 0;
-        for(Connection c1_aux : c1) {
-            for(Connection c2_aux : c2)
-                if(c2_aux.mServerName.equals(c1_aux.mServerName)) {
-                    same_connections++;
-                    break;
-                }
-        }
-        return c1.length == c2.length && c1.length == same_connections;
-
+    private void updateGateways(){
+        gateways_manager.fromEipServiceJson(eip_definition);
+        gatewaysToPreferences();
     }
 
-    private void removeDuplicatedGateway(VpnProfile profile) {
-        Iterator<Gateway> it = gateways.iterator();
-        List<Gateway> gateways_to_remove = new ArrayList<>();
-        while(it.hasNext()) {
-            Gateway aux = it.next();
-            if(sameConnections(aux.getProfile().mConnections, profile.mConnections)) {
-                gateways_to_remove.add(aux);
-	    }
-        }
-        gateways.removeAll(gateways_to_remove);
+    private void gatewaysFromPreferences() {
+        String gateways_string = preferences.getString(Gateway.TAG, "");
+        gateways_manager.addFromString(gateways_string);
+        preferences.edit().remove(Gateway.TAG).apply();
     }
 
     private void gatewaysToPreferences() {
-        Type type_list_gateways = new TypeToken<List<Gateway>>() {}.getType();
-        String gateways_string = new Gson().toJson(gateways, type_list_gateways);
-        preferences.edit().putString(Gateway.TAG, gateways_string).apply();
+        String gateways_string = gateways_manager.toString();
+        preferences.edit().putString(Gateway.TAG, gateways_string).commit();
     }
 
     private void checkCertValidity() {
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
index daf7d4a7..0d8a2f7b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -20,6 +20,8 @@ import android.app.Activity;
 import android.content.SharedPreferences;
 import android.util.Log;
 
+import com.google.gson.Gson;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -40,7 +42,7 @@ import se.leap.bitmaskclient.Dashboard;
  */
 public class Gateway {
 		
-    public static String TAG = Gateway.class.getSimpleName();
+    public final static String TAG = Gateway.class.getSimpleName();
 
     private JSONObject general_configuration;
     private JSONObject secrets;
@@ -53,7 +55,7 @@ public class Gateway {
      * Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json
      * and create a VpnProfile belonging to it.
      */
-    protected Gateway(JSONObject eip_definition, JSONObject secrets, JSONObject gateway){
+    public Gateway(JSONObject eip_definition, JSONObject secrets, JSONObject gateway){
 
 	this.gateway = gateway;
         this.secrets = secrets;
@@ -130,4 +132,9 @@ public class Gateway {
     public int getTimezone() {
 	return timezone;
     }
+
+    @Override
+    public String toString() {
+        return new Gson().toJson(this, Gateway.class);
+    }
 }
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
new file mode 100644
index 00000000..b1aa5a2f
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
@@ -0,0 +1,184 @@
+/**
+ * Copyright (c) 2013, 2014, 2015 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.*;
+import android.util.Log;
+
+import com.google.gson.*;
+import com.google.gson.reflect.*;
+
+import org.json.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import de.blinkt.openvpn.*;
+import de.blinkt.openvpn.core.*;
+import se.leap.bitmaskclient.*;
+
+/**
+ * @author parmegv
+ */
+public class GatewaysManager {
+
+    private Context context;
+    private SharedPreferences preferences;
+    private List<Gateway> gateways = new ArrayList<>();
+    private ProfileManager profile_manager;
+    private Type list_type = new TypeToken<ArrayList<Gateway>>() {}.getType();
+
+    public GatewaysManager() {}
+
+    public GatewaysManager(Context context, SharedPreferences preferences) {
+        this.context = context;
+        this.preferences = preferences;
+        profile_manager = ProfileManager.getInstance(context);
+    }
+    public Gateway select() {
+        GatewaySelector gateway_selector = new GatewaySelector(gateways);
+        return gateway_selector.select();
+    }
+
+    public boolean isEmpty() {
+        return gateways.isEmpty();
+    }
+
+    public int size() {
+        return gateways.size();
+    }
+
+    public void addFromString(String gateways) {
+        List<Gateway> gateways_list = new ArrayList<Gateway>();
+        try {
+            gateways_list = new Gson().fromJson(gateways, list_type);
+        } catch(JsonSyntaxException e) {
+            gateways_list.add(new Gson().fromJson(gateways, Gateway.class));
+        }
+
+        if(gateways_list != null) {
+            for (Gateway gateway : gateways_list)
+                removeDuplicatedGateway(gateway);
+            this.gateways.addAll(gateways_list);
+        } else
+            Log.d("GatewaysManager", "No gateways added");
+    }
+
+    @Override
+    public String toString() {
+        return new Gson().toJson(gateways, list_type);
+    }
+
+    public void fromEipServiceJson(JSONObject eip_definition) {
+        try {
+	    JSONArray gatewaysDefined = eip_definition.getJSONArray("gateways");
+	    for (int i = 0; i < gatewaysDefined.length(); i++) {
+		JSONObject gw = gatewaysDefined.getJSONObject(i);
+		if (isOpenVpnGateway(gw)) {
+		    JSONObject secrets = secretsConfiguration();
+		    Gateway aux = new Gateway(eip_definition, secrets, gw);
+		    if(!containsProfileWithSecrets(aux.getProfile())) {
+			addGateway(aux);
+		    }
+		}
+	    }
+	} 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 JSONObject secretsConfiguration() {
+        JSONObject result = new JSONObject();
+        try {
+            result.put(Provider.CA_CERT, preferences.getString(Provider.CA_CERT, ""));
+            result.put(Constants.PRIVATE_KEY, preferences.getString(Constants.PRIVATE_KEY, ""));
+            result.put(Constants.CERTIFICATE, preferences.getString(Constants.CERTIFICATE, ""));
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    private boolean containsProfileWithSecrets(VpnProfile profile) {
+        boolean result = false;
+
+        Collection<VpnProfile> profiles = profile_manager.getProfiles();
+        for(VpnProfile aux : profiles) {
+            result = result || sameConnections(profile.mConnections, aux.mConnections)
+		&& profile.mClientCertFilename.equalsIgnoreCase(aux.mClientCertFilename)
+		&& profile.mClientKeyFilename.equalsIgnoreCase(aux.mClientKeyFilename);
+        }
+        return result;
+    }
+
+    private void addGateway(Gateway gateway) {
+        removeDuplicatedGateway(gateway);
+
+        gateways.add(gateway);
+
+        VpnProfile profile = gateway.getProfile();
+        profile_manager.addProfile(profile);
+        //profile_manager.saveProfile(context, profile);
+        //profile_manager.saveProfileList(context);
+    }
+
+    private void removeDuplicatedGateway(Gateway gateway) {
+        Iterator<Gateway> it = gateways.iterator();
+        List<Gateway> gateways_to_remove = new ArrayList<>();
+        while(it.hasNext()) {
+            Gateway aux = it.next();
+            if(sameConnections(aux.getProfile().mConnections, gateway.getProfile().mConnections)) {
+                gateways_to_remove.add(aux);
+            }
+        }
+        gateways.removeAll(gateways_to_remove);
+        removeDuplicatedProfiles(gateway.getProfile());
+    }
+
+    private void removeDuplicatedProfiles(VpnProfile original) {
+	Collection<VpnProfile> profiles = profile_manager.getProfiles();
+        List<VpnProfile> remove_list = new ArrayList<>();
+	for(VpnProfile aux : profiles) {
+	    if (sameConnections(original.mConnections, aux.mConnections))
+		remove_list.add(aux);
+	}
+        for (VpnProfile profile : remove_list)
+            profile_manager.removeProfile(context, profile);
+    }
+
+    private boolean sameConnections(Connection[] c1, Connection[] c2) {
+        int same_connections = 0;
+        for(Connection c1_aux : c1) {
+            for(Connection c2_aux : c2)
+                if(c2_aux.mServerName.equals(c1_aux.mServerName)) {
+                    same_connections++;
+                    break;
+                }
+        }
+        return c1.length == c2.length && c1.length == same_connections;
+    }
+}
-- 
cgit v1.2.3


From b3ea137781af2eacd0a5b75f24030cfbc3c9d962 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Tue, 13 Jan 2015 13:48:34 +0100
Subject: Reusing methods to download certificate.

This makes sure that if a a certificate is not valid, a new one is
downloaded. Unit tests don't apply to this, I've already added one to
the certificate validator, whose result will determine if a new
certificate will be downloaded.
---
 .../main/java/se/leap/bitmaskclient/Dashboard.java | 21 +++++----
 .../java/se/leap/bitmaskclient/EipFragment.java    | 54 ++++++++++------------
 2 files changed, 36 insertions(+), 39 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index 4e6120ab..94be7cd1 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -296,11 +296,14 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	providerApiCommand(Bundle.EMPTY, R.string.logout_message, ProviderAPI.LOG_OUT);
     }
 
-    private void downloadAuthedUserCertificate() {
-	Bundle parameters = new Bundle();
-	parameters.putString(ConfigurationWizard.TYPE_OF_CERTIFICATE, ConfigurationWizard.AUTHED_CERTIFICATE);
-	
-	providerApiCommand(parameters, R.string.downloading_certificate_message, ProviderAPI.DOWNLOAD_CERTIFICATE);
+    protected void downloadVpnCertificate() {
+        boolean is_authenticated = !LeapSRPSession.getToken().isEmpty();
+        boolean allowed_anon = preferences.getBoolean(Constants.ALLOWED_ANON, false);
+        if(allowed_anon || is_authenticated)
+            providerApiCommand(Bundle.EMPTY, R.string.downloading_certificate_message, ProviderAPI.DOWNLOAD_CERTIFICATE);
+        else
+            sessionDialog(Bundle.EMPTY);
+
     }
 
     private Bundle bundleParameters(String username, String password) {
@@ -312,8 +315,8 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	return parameters;
     }
 
-    private void providerApiCommand(Bundle parameters, int progressbar_message_resId, String providerApi_action) {
-        if(eip_fragment != null) {
+    protected void providerApiCommand(Bundle parameters, int progressbar_message_resId, String providerApi_action) {
+        if(eip_fragment != null && progressbar_message_resId != 0) {
             eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
             setStatusMessage(progressbar_message_resId);
         }
@@ -322,7 +325,7 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	startService(command);
     }
 
-    protected Intent prepareProviderAPICommand(Bundle parameters, String action) {
+    private Intent prepareProviderAPICommand(Bundle parameters, String action) {
 	providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
 	providerAPI_result_receiver.setReceiver(this);
 	
@@ -371,7 +374,7 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	    preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
 
 	    updateViewHidingProgressBar(resultCode);
-	    downloadAuthedUserCertificate();
+	    downloadVpnCertificate();
 	} else if(resultCode == ProviderAPI.FAILED_LOGIN) {
 	    updateViewHidingProgressBar(resultCode);
 	    sessionDialog(resultData);
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
index 54432033..bbcaa577 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -45,7 +45,7 @@ public class EipFragment extends Fragment implements Observer {
     @InjectView(R.id.eipProgress)
     ProgressBar progress_bar;
 
-    private static Activity parent_activity;
+    private static Dashboard dashboard;
     private static EIPReceiver mEIPReceiver;
     private static EipStatus eip_status;
     private boolean is_starting_to_connect;
@@ -53,11 +53,9 @@ public class EipFragment extends Fragment implements Observer {
     @Override
     public void onAttach(Activity activity) {
 	super.onAttach(activity);
-	parent_activity = activity;
 
-        Dashboard dashboard = (Dashboard) parent_activity;
-        Intent provider_API_command = dashboard.prepareProviderAPICommand(Bundle.EMPTY, ProviderAPI.DOWNLOAD_EIP_SERVICE);
-        parent_activity.startService(provider_API_command);
+        dashboard = (Dashboard) activity;
+        dashboard.providerApiCommand(Bundle.EMPTY, 0, ProviderAPI.DOWNLOAD_EIP_SERVICE);
     }
     
     @Override
@@ -130,7 +128,6 @@ public class EipFragment extends Fragment implements Observer {
 	    startEipFromScratch();
 	else if(canLogInToStartEIP()) {
 	    Log.d(TAG, "Can Log In to start EIP");
-	    Dashboard dashboard = (Dashboard) parent_activity;
         Bundle bundle = new Bundle();
         bundle.putBoolean(IS_PENDING, true);
 	    dashboard.sessionDialog(bundle);
@@ -160,16 +157,16 @@ public class EipFragment extends Fragment implements Observer {
     }
 
     private void askPendingStartCancellation() {	
-	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))
+	AlertDialog.Builder alertBuilder = new AlertDialog.Builder(dashboard);
+	alertBuilder.setTitle(dashboard.getString(R.string.eip_cancel_connect_title))
+	    .setMessage(dashboard.getString(R.string.eip_cancel_connect_text))
 	    .setPositiveButton((R.string.yes), new DialogInterface.OnClickListener() {
 		    @Override
 		    public void onClick(DialogInterface dialog, int which) {
 			askToStopEIP();
 		    }
 		})
-	    .setNegativeButton(parent_activity.getString(R.string.no), new DialogInterface.OnClickListener() {
+	    .setNegativeButton(dashboard.getString(R.string.no), new DialogInterface.OnClickListener() {
 		    @Override
 		    public void onClick(DialogInterface dialog, int which) {
 			eip_switch.setChecked(true);
@@ -182,7 +179,7 @@ public class EipFragment extends Fragment implements Observer {
         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);
+	String status = dashboard.getString(R.string.eip_status_start_pending);
 	status_message.setText(status);
 	
 	if(!eip_switch.isChecked()) {
@@ -195,15 +192,15 @@ public class EipFragment extends Fragment implements Observer {
     private void stopEIP() {
 	if(eip_status.isConnecting())
 	    VoidVpnService.stop();
-	Intent disconnect_vpn = new Intent(parent_activity, DisconnectVPN.class);
-	parent_activity.startActivityForResult(disconnect_vpn, EIP.DISCONNECT);
+	Intent disconnect_vpn = new Intent(dashboard, DisconnectVPN.class);
+	dashboard.startActivityForResult(disconnect_vpn, EIP.DISCONNECT);
 	eip_status.setDisconnecting();
     }
 
     protected void askToStopEIP() {
         hideProgressBar();
 
-	String status = parent_activity.getString(R.string.eip_state_not_connected);
+	String status = dashboard.getString(R.string.eip_state_not_connected);
 	status_message.setText(status);
 
 	eipCommand(Constants.ACTION_STOP_EIP);
@@ -217,10 +214,10 @@ public class EipFragment extends Fragment implements Observer {
      */
     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);
+	Intent vpn_intent = new Intent(dashboard.getApplicationContext(), EIP.class);
 	vpn_intent.setAction(action);
 	vpn_intent.putExtra(Constants.RECEIVER_TAG, mEIPReceiver);
-	parent_activity.startService(vpn_intent);
+	dashboard.startService(vpn_intent);
     }
 	
     @Override
@@ -228,7 +225,7 @@ public class EipFragment extends Fragment implements Observer {
 	if(observable instanceof EipStatus) {
 	    eip_status = (EipStatus) observable;
 	    final EipStatus eip_status = (EipStatus) observable;
-	    parent_activity.runOnUiThread(new Runnable() {
+	    dashboard.runOnUiThread(new Runnable() {
 	    	    @Override
 	    	    public void run() {
 			handleNewState(eip_status);
@@ -254,13 +251,13 @@ public class EipFragment extends Fragment implements Observer {
 	Log.d(TAG, "setConnectedUi? " + eip_status.isConnected());
 	adjustSwitch();
     is_starting_to_connect = false;
-	status_message.setText(parent_activity.getString(R.string.eip_state_connected));
+	status_message.setText(dashboard.getString(R.string.eip_state_connected));
     }
 
     private void setDisconnectedUI(){
 	hideProgressBar();
 	adjustSwitch();
-	status_message.setText(parent_activity.getString(R.string.eip_state_not_connected));
+	status_message.setText(dashboard.getString(R.string.eip_state_not_connected));
     }
 
     private void adjustSwitch() {
@@ -281,13 +278,18 @@ public class EipFragment extends Fragment implements Observer {
     private void setInProgressUI(EipStatus eip_status) {
 	int localizedResId = eip_status.getLocalizedResId();
 	String logmessage = eip_status.getLogMessage();
-	String prefix = parent_activity.getString(localizedResId);
+	String prefix = dashboard.getString(localizedResId);
 
 	status_message.setText(prefix + " " + logmessage);
         is_starting_to_connect = false;
 	adjustSwitch();
     }
 
+    private void updatingCertificateUI() {
+        progress_bar.setVisibility(View.VISIBLE);
+        status_message.setText(getString(R.string.updating_certificate_message));
+    }
+
     private void hideProgressBar() {
 	if(progress_bar != null)
 	    progress_bar.setVisibility(View.GONE);
@@ -333,16 +335,8 @@ public class EipFragment extends Fragment implements Observer {
 		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.sessionDialog(Bundle.EMPTY);
-		    } else {
-			Intent provider_API_command = dashboard.prepareProviderAPICommand(Bundle.EMPTY, ProviderAPI.DOWNLOAD_CERTIFICATE);
-			parent_activity.startService(provider_API_command);
-		    }
+            updatingCertificateUI();
+            dashboard.downloadVpnCertificate();
 		    break;
 		}
 	    }
-- 
cgit v1.2.3


From 744272dd1d43913c8112d1dedcb0b5a9023c491e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Wed, 14 Jan 2015 17:18:19 +0100
Subject: Connect if logged in after turning switch on

---
 .../main/java/se/leap/bitmaskclient/Dashboard.java | 49 ++++++++++------------
 .../java/se/leap/bitmaskclient/EipFragment.java    | 20 ++++++++-
 .../main/java/se/leap/bitmaskclient/eip/EIP.java   |  5 +--
 3 files changed, 42 insertions(+), 32 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index 94be7cd1..afe1a638 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -127,33 +127,28 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	    case 91: // 0.6.0 without Bug #5999
 	    case 101: // 0.8.0
 		if(!preferences.getString(Constants.KEY, "").isEmpty())
-		    updateEipService();
+		    eip_fragment.updateEipService();
 		break;
 	    }
 	} catch (NameNotFoundException e) {
 	    Log.d(TAG, "Handle version didn't find any " + getPackageName() + " package");
 	}
     }
-    
-    @SuppressLint("CommitPrefEdits")
+
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data){
 	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)) {
-		    sessionDialog(Bundle.EMPTY);
-		}
+	    if ( resultCode == RESULT_OK && data.hasExtra(Provider.KEY)) {
+                provider = data.getParcelableExtra(Provider.KEY);
+                providerToPreferences(provider);
+
+                buildDashboard(false);
+                invalidateOptionsMenu();
+                if (data.hasExtra(SessionDialog.TAG)) {
+                    sessionDialog(Bundle.EMPTY);
+                }
+
+                preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
 	    } else if (resultCode == RESULT_CANCELED && data.hasExtra(ACTION_QUIT)) {
                 finish();
 	    } else
@@ -163,6 +158,13 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	}
     }
 
+    @SuppressLint("CommitPrefEdits")
+    private void providerToPreferences(Provider provider) {
+        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();
+    }
+
     private void configErrorDialog() {
 	AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getAppContext());
 	alertBuilder.setTitle(getResources().getString(R.string.setup_error_title));
@@ -389,16 +391,15 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	    setResult(RESULT_CANCELED);
 	} else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) {
 	    updateViewHidingProgressBar(resultCode);
-	    updateEipService();
+	    eip_fragment.updateEipService();
 	    setResult(RESULT_OK);
 	} else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
 	    updateViewHidingProgressBar(resultCode);
 	    setResult(RESULT_CANCELED);
 	}
 	else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) {
+        eip_fragment.updateEipService();
 	    setResult(RESULT_OK);
-
-	    updateEipService();
 	} else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) {
 	    setResult(RESULT_CANCELED);
 	}
@@ -410,12 +411,6 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
 	invalidateOptionsMenu();
     }
 
-    private void updateEipService() {
-	Intent updateEIP = new Intent(getApplicationContext(), EIP.class);
-	updateEIP.setAction(Constants.ACTION_UPDATE_EIP_SERVICE);
-	startService(updateEIP);
-    }
-
     private void changeStatusMessage(final int previous_result_code) {
 	ResultReceiver status_receiver = new ResultReceiver(new Handler()){
 		protected void onReceiveResult(int resultCode, Bundle resultData){
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
index bbcaa577..b2898249 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -49,6 +49,7 @@ public class EipFragment extends Fragment implements Observer {
     private static EIPReceiver mEIPReceiver;
     private static EipStatus eip_status;
     private boolean is_starting_to_connect;
+    private boolean wants_to_connect;
 
     @Override
     public void onAttach(Activity activity) {
@@ -127,7 +128,7 @@ public class EipFragment extends Fragment implements Observer {
 	if(canStartEIP())
 	    startEipFromScratch();
 	else if(canLogInToStartEIP()) {
-	    Log.d(TAG, "Can Log In to start EIP");
+        wants_to_connect = true;
         Bundle bundle = new Bundle();
         bundle.putBoolean(IS_PENDING, true);
 	    dashboard.sessionDialog(bundle);
@@ -176,6 +177,7 @@ public class EipFragment extends Fragment implements Observer {
     }
 
     public void startEipFromScratch() {
+        wants_to_connect = false;
         is_starting_to_connect = true;
         progress_bar.setVisibility(View.VISIBLE);
 	eip_switch.setVisibility(View.VISIBLE);
@@ -205,6 +207,10 @@ public class EipFragment extends Fragment implements Observer {
 
 	eipCommand(Constants.ACTION_STOP_EIP);
     }
+
+    protected void updateEipService() {
+        eipCommand(Constants.ACTION_UPDATE_EIP_SERVICE);
+    }
 	
     /**
      * Send a command to EIP
@@ -339,7 +345,17 @@ public class EipFragment extends Fragment implements Observer {
             dashboard.downloadVpnCertificate();
 		    break;
 		}
-	    }
+	    } else if (request.equals(Constants.ACTION_UPDATE_EIP_SERVICE)) {
+            switch (resultCode) {
+                case Activity.RESULT_OK:
+                    if(wants_to_connect)
+                        startEipFromScratch();
+                    break;
+                case Activity.RESULT_CANCELED:
+                    handleNewState(eip_status);
+                    break;
+            }
+        }
 	}
     }
 
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index a5cc817a..3b72a486 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -64,10 +64,8 @@ public final class EIP extends IntentService {
 	context = getApplicationContext();
         preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
 	eip_definition = eipDefinitionFromPreferences();
-        if(gateways_manager.isEmpty()) {
-            gateways_manager = new GatewaysManager(context, preferences);
+        if(gateways_manager.isEmpty())
             gatewaysFromPreferences();
-        }
     }
 
     @Override
@@ -179,6 +177,7 @@ public final class EIP extends IntentService {
 
     private void gatewaysFromPreferences() {
         String gateways_string = preferences.getString(Gateway.TAG, "");
+        gateways_manager = new GatewaysManager(context, preferences);
         gateways_manager.addFromString(gateways_string);
         preferences.edit().remove(Gateway.TAG).apply();
     }
-- 
cgit v1.2.3


From 250b56762857f2e52626236d3716459786ce126d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Parm=C3=A9nides=20GV?= <parmegv@sdf.org>
Date: Thu, 15 Jan 2015 15:24:52 +0100
Subject: More tests, sometimes they don't pass sometimes do

testIsValid depends on the speed of Runtime's exec: sometimes it's quick
and passes (because it changes the date correctly) and sometimes is
not. A waitFor doesn't fix the issue, it waits forever.
---
 app/src/main/java/se/leap/bitmaskclient/EipFragment.java | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'app/src/main/java')

diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
index b2898249..588b137b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -37,7 +37,6 @@ public class EipFragment extends Fragment implements Observer {
     protected static final String STATUS_MESSAGE = TAG + ".status_message";
     public static final String START_ON_BOOT = "start on boot";
 
-    private View view;
     @InjectView(R.id.eipSwitch)
     Switch eip_switch;
     @InjectView(R.id.status_message)
@@ -69,7 +68,7 @@ public class EipFragment extends Fragment implements Observer {
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-	view = inflater.inflate(R.layout.eip_service_fragment, container, false);
+	View view = inflater.inflate(R.layout.eip_service_fragment, container, false);
         ButterKnife.inject(this, view);
 
 	if (eip_status.isConnecting())
-- 
cgit v1.2.3