diff options
author | Parménides GV <parmegv@ma.sdf.org> | 2013-06-24 19:37:35 +0200 |
---|---|---|
committer | Parménides GV <parmegv@ma.sdf.org> | 2013-06-24 19:37:35 +0200 |
commit | 50db5b5816476751f39bb7b8adc927562a0cc677 (patch) | |
tree | fa8fc602518430137895fbdbaba439f6c3d92321 /src/se/leap/leapclient | |
parent | 195d5ab2d518f6f3960edd3c636e941830c2664d (diff) | |
parent | 304b29d6cb85d710756a56bc9c009429b8407d69 (diff) |
Merge branch 'feature/eip' into develop
Diffstat (limited to 'src/se/leap/leapclient')
-rw-r--r-- | src/se/leap/leapclient/ConfigHelper.java | 5 | ||||
-rw-r--r-- | src/se/leap/leapclient/Dashboard.java | 296 | ||||
-rw-r--r-- | src/se/leap/leapclient/EIP.java | 481 | ||||
-rw-r--r-- | src/se/leap/leapclient/Provider.java | 3 | ||||
-rw-r--r-- | src/se/leap/leapclient/ProviderAPI.java | 14 |
5 files changed, 726 insertions, 73 deletions
diff --git a/src/se/leap/leapclient/ConfigHelper.java b/src/se/leap/leapclient/ConfigHelper.java index dd20112c..cab5fdee 100644 --- a/src/se/leap/leapclient/ConfigHelper.java +++ b/src/se/leap/leapclient/ConfigHelper.java @@ -53,6 +53,7 @@ public class ConfigHelper { ALLOWED_ANON = "allow_anonymous", MAIN_CERT_KEY = "main_cert", CERT_KEY = "cert", + KEY_KEY = "key", EIP_SERVICE_KEY = "eip", TYPE_OF_CERTIFICATE = "type_of_certificate", ANON_CERTIFICATE = "anon_certificate", @@ -72,7 +73,9 @@ public class ConfigHelper { PASSWORD_KEY = "password", ALLOW_REGISTRATION_KEY = "allow_registration", EIP_SERVICE_API_PATH = "config/eip-service.json", - ERRORS_KEY = "errors" + ERRORS_KEY = "errors", + RECEIVER_TAG = "receiverTag", + REQUEST_TAG = "requestTag"; ; final public static String NG_1024 = diff --git a/src/se/leap/leapclient/Dashboard.java b/src/se/leap/leapclient/Dashboard.java index dd8ee335..3c9df56c 100644 --- a/src/se/leap/leapclient/Dashboard.java +++ b/src/se/leap/leapclient/Dashboard.java @@ -6,9 +6,11 @@ import org.json.JSONException; import org.json.JSONObject; import se.leap.leapclient.ProviderAPIResultReceiver.Receiver; -import se.leap.leapclient.R; import se.leap.openvpn.AboutFragment; +import se.leap.openvpn.LogWindow; import se.leap.openvpn.MainActivity; +import se.leap.openvpn.OpenVPN; +import se.leap.openvpn.OpenVPN.StateListener; import android.app.Activity; import android.app.AlertDialog; import android.app.DialogFragment; @@ -20,17 +22,25 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; +import android.os.ResultReceiver; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; import android.widget.CompoundButton; +import android.widget.RelativeLayout; import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; -public class Dashboard extends Activity implements LogInDialog.LogInDialogInterface, Receiver { +/** + * The main user facing Activity of LEAP Android, consisting of status, controls, + * and access to preferences. + * + * @author Sean Leonard <meanderingcode@aetherislands.net> + */ +public class Dashboard extends Activity implements LogInDialog.LogInDialogInterface,Receiver,StateListener { protected static final int CONFIGURE_LEAP = 0; @@ -40,8 +50,14 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf private TextView providerNameTV; private TextView eipTypeTV; + private Switch eipSwitch; + private View eipDetail; + private TextView eipStatus; + + private boolean mEipWait = false; public ProviderAPIResultReceiver providerAPI_result_receiver; + private EIPReceiver mEIPReceiver; @Override protected void onCreate(Bundle savedInstanceState) { @@ -55,89 +71,150 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf if(ConfigHelper.shared_preferences == null) ConfigHelper.setSharedPreferences(preferences); - // Check if we have preferences, run configuration wizard if not - // TODO We should do a better check for config that this! if (preferences.contains("provider") && preferences.getString(ConfigHelper.PROVIDER_KEY, null) != null) buildDashboard(); else startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP); } + + @Override + protected void onStop() { + super.onStop(); + if (provider != null) + if (provider.hasEIP() && provider.getEIPType() == "OpenVPN") + OpenVPN.removeStateListener(this); + } + + @Override + protected void onResume() { + super.onResume(); + if (provider != null) + if (provider.hasEIP() && provider.getEIPType() == "OpenVPN") + OpenVPN.addStateListener(this); + } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data){ if ( requestCode == CONFIGURE_LEAP ) { if ( resultCode == RESULT_OK ){ - // Configuration done, get our preferences again - preferences = getSharedPreferences(ConfigHelper.PREFERENCES_KEY,MODE_PRIVATE); - buildDashboard(); - } else { - // Something went wrong in configuration - AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getAppContext()); - alertBuilder.setTitle(getResources().getString(R.string.setup_error_title)); - alertBuilder - .setMessage(getResources().getString(R.string.setup_error_text)) - .setCancelable(false) - .setPositiveButton(getResources().getString(R.string.setup_error_configure_button), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - startActivityForResult(new Intent(getAppContext(),ConfigurationWizard.class),CONFIGURE_LEAP); - } - }) - .setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - SharedPreferences.Editor prefsEdit = getSharedPreferences(ConfigHelper.PREFERENCES_KEY, MODE_PRIVATE).edit(); - prefsEdit.remove(ConfigHelper.PROVIDER_KEY).commit(); - finish(); - } - }); - } + } else + configErrorDialog(); } } + /** + * Dialog shown when encountering a configuration error. Such errors require + * reconfiguring LEAP or aborting the application. + */ + private void configErrorDialog() { + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getAppContext()); + alertBuilder.setTitle(getResources().getString(R.string.setup_error_title)); + alertBuilder + .setMessage(getResources().getString(R.string.setup_error_text)) + .setCancelable(false) + .setPositiveButton(getResources().getString(R.string.setup_error_configure_button), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + startActivityForResult(new Intent(getAppContext(),ConfigurationWizard.class),CONFIGURE_LEAP); + } + }) + .setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences.Editor prefsEdit = getSharedPreferences(ConfigHelper.PREFERENCES_KEY, MODE_PRIVATE).edit(); + prefsEdit.remove(ConfigHelper.PROVIDER_KEY).commit(); + finish(); + } + }); + } + + /** + * Inflates permanent UI elements of the View and contains logic for what + * service dependent UI elements to include. + */ private void buildDashboard() { - // Get our provider - provider = Provider.getInstance(); - provider.init( this ); - - // Set provider name in textview - providerNameTV = (TextView) findViewById(R.id.providerName); - providerNameTV.setText(provider.getName()); - providerNameTV.setTextSize(28); // TODO maybe to some calculating, or a marquee? - - // TODO Inflate layout fragments for provider's services - if ( provider.hasEIP() ) - serviceItemEIP(); + provider = Provider.getInstance(); + provider.init( this ); + + providerNameTV = (TextView) findViewById(R.id.providerName); + providerNameTV.setText(provider.getName()); + providerNameTV.setTextSize(28); + + if ( provider.hasEIP() ){ + startService( new Intent(EIP.ACTION_UPDATE_EIP_SERVICE) ); + if (provider.getEIPType() == "OpenVPN") + OpenVPN.addStateListener(this); + serviceItemEIP(); + } } + /** + * Builds the UI for the EIP service Dashboard component + */ private void serviceItemEIP() { - // FIXME Provider service (eip/openvpn) + mEIPReceiver = new EIPReceiver(new Handler()); + mEIPReceiver.setReceiver(this); + + Intent intent = new Intent(this,EIP.class); + intent.setAction(EIP.ACTION_IS_EIP_RUNNING); + intent.putExtra(ConfigHelper.RECEIVER_TAG, mEIPReceiver); + startService(intent); + ((ViewStub) findViewById(R.id.eipOverviewStub)).inflate(); - // Set our EIP type title eipTypeTV = (TextView) findViewById(R.id.eipType); eipTypeTV.setText(provider.getEIPType()); + + eipDetail = ((RelativeLayout) findViewById(R.id.eipDetail)); + View eipSettings = findViewById(R.id.eipSettings); + eipSettings.setVisibility(View.GONE); // FIXME too! + eipDetail.setVisibility(View.VISIBLE); + eipStatus = (TextView) findViewById(R.id.eipStatus); - // TODO Bind our switch to run our EIP - // What happens when our VPN stops running? does it call the listener? - Switch eipSwitch = (Switch) findViewById(R.id.eipSwitch); + eipSwitch = (Switch) findViewById(R.id.eipSwitch); eipSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if ( isChecked ){ - //TODO startVPN(); - } else { - //TODO stopVPN(); + if (!mEipWait){ + buttonView.setClickable(false); + mEipWait = true; + + Intent vpnIntent; + if (isChecked){ + vpnIntent = new Intent(EIP.ACTION_START_EIP); + } else { + vpnIntent = new Intent(EIP.ACTION_STOP_EIP); + } + vpnIntent.putExtra(ConfigHelper.RECEIVER_TAG, mEIPReceiver); + startService(vpnIntent); } } }); - - //TODO write our info into the view fragment that will expand with details and a settings button - // TODO set eip overview subview - // TODO make eip type clickable, show subview - // TODO attach vpn status feedback to eip overview view - // TODO attach settings button to something + } + + /** + * Expands the EIP Dashboard component for extra details view. + * Called by onClick property in client_dashboard.xml layout. + * + * @param view (Unused) The View calling this method by its onClick property + */ + public void toggleEipOverview(View view) { + if (eipDetail.isShown()) + eipDetail.setVisibility(View.GONE); + else + eipDetail.setVisibility(View.VISIBLE); + } + + /** + * Launches the se.leap.openvpn.LogWindow Activity showing detailed OpenVPN log + * + * @param view (Unused) The View calling this method by its onClick property + */ + public void showEIPLog(View view){ + Intent intent = new Intent(getBaseContext(),LogWindow.class); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + startActivity(intent); } @Override @@ -159,7 +236,6 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf @Override public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.client_dashboard, menu); return true; } @@ -167,21 +243,15 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf @Override public boolean onOptionsItemSelected(MenuItem item){ Intent intent; - // Handle item selection switch (item.getItemId()){ case R.id.about_leap: - // TODO move se.leap.openvpn.AboutFragment into our package Fragment aboutFragment = new AboutFragment(); FragmentTransaction trans = getFragmentManager().beginTransaction(); trans.replace(R.id.dashboardLayout, aboutFragment); trans.addToBackStack(null); trans.commit(); - - //intent = new Intent(this,AboutFragment.class); - //startActivity(intent); return true; case R.id.legacy_interface: - // TODO call se.leap.openvpn.MainActivity intent = new Intent(this,MainActivity.class); startActivity(intent); return true; @@ -197,11 +267,6 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf } } - - @SuppressWarnings("unused") - private void toggleOverview() { - // TODO Expand the one line overview item to show some details - } @Override public void authenticate(String username, String password) { @@ -319,8 +384,101 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf } } - // Used for getting Context when outside of a class extending Context + /** + * 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; } + + @Override + public void updateState(final String state, final String logmessage, final int localizedResId) { + // Note: "states" are not organized anywhere...collected state strings: + // NOPROCESS,NONETWORK,BYTECOUNT,AUTH_FAILED + some parsing thing ( WAIT(?),AUTH,GET_CONFIG,ASSIGN_IP,CONNECTED(?) ) + runOnUiThread(new Runnable() { + + @Override + public void run() { + if (eipStatus != null) { + String prefix = getString(localizedResId) + ":"; + if (state.equals("BYTECOUNT") || state.equals("NOPROCESS")) + prefix = ""; + eipStatus.setText(prefix + logmessage); + } + } + }); + } + + /** + * Inner class for handling messages related to EIP status and control requests + * + * @author Sean Leonard <meanderingcode@aetherislands.net> + */ + protected class EIPReceiver extends ResultReceiver { + + Dashboard mDashboard; + + protected EIPReceiver(Handler handler){ + super(handler); + } + + public void setReceiver(Dashboard receiver) { + mDashboard = receiver; + } + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + super.onReceiveResult(resultCode, resultData); + + String request = resultData.getString(ConfigHelper.REQUEST_TAG); + mEipWait = true; + boolean checked = false; + + if (request == EIP.ACTION_IS_EIP_RUNNING) { + switch (resultCode){ + case RESULT_OK: + checked = true; + break; + case RESULT_CANCELED: + checked = false; + break; + } + } else if (request == EIP.ACTION_START_EIP) { + switch (resultCode){ + case RESULT_OK: + checked = true; + break; + case RESULT_CANCELED: + checked = false; + break; + } + } else if (request == EIP.ACTION_STOP_EIP) { + switch (resultCode){ + case RESULT_OK: + checked = false; + break; + case RESULT_CANCELED: + checked = true; + break; + } + } else if (request == EIP.EIP_NOTIFICATION) { + switch (resultCode){ + case RESULT_OK: + checked = true; + break; + case RESULT_CANCELED: + checked = false; + break; + } + } + + Switch eipS = ((Switch) mDashboard.findViewById(R.id.eipSwitch)); + eipS.setChecked(checked); + eipS.setClickable(true); + mEipWait = false; + } + } } diff --git a/src/se/leap/leapclient/EIP.java b/src/se/leap/leapclient/EIP.java new file mode 100644 index 00000000..426f4359 --- /dev/null +++ b/src/se/leap/leapclient/EIP.java @@ -0,0 +1,481 @@ +package se.leap.leapclient; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Vector; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import se.leap.openvpn.ConfigParser; +import se.leap.openvpn.ConfigParser.ConfigParseError; +import se.leap.openvpn.LaunchVPN; +import se.leap.openvpn.OpenVpnManagementThread; +import se.leap.openvpn.OpenVpnService; +import se.leap.openvpn.OpenVpnService.LocalBinder; +import se.leap.openvpn.ProfileManager; +import se.leap.openvpn.VpnProfile; + +import android.app.Activity; +import android.app.IntentService; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ResultReceiver; +import android.util.Log; + +/** + * EIP is the abstract base class for interacting with and managing the Encrypted + * Internet Proxy connection. Connections are started, stopped, and queried through + * this IntentService. + * Contains logic for parsing eip-service.json from the provider, configuring and selecting + * gateways, and controlling {@link .openvpn.OpenVpnService} connections. + * + * @author Sean Leonard <meanderingcode@aetherislands.net> + */ +public final class EIP extends IntentService { + + public final static String ACTION_START_EIP = "se.leap.leapclient.START_EIP"; + public final static String ACTION_STOP_EIP = "se.leap.leapclient.STOP_EIP"; + public final static String ACTION_UPDATE_EIP_SERVICE = "se.leap.leapclient.UPDATE_EIP_SERVICE"; + public final static String ACTION_IS_EIP_RUNNING = "se.leap.leapclient.IS_RUNNING"; + public final static String EIP_NOTIFICATION = "EIP_NOTIFICATION"; + + private static Context context; + private static ResultReceiver mReceiver; + private static OpenVpnService mVpnService; + private static boolean mBound = false; + // Used to store actions to "resume" onServiceConnection + private static String mPending = null; + + private static JSONObject eipDefinition = null; + + private static OVPNGateway activeGateway = null; + + public EIP(){ + super("LEAPEIP"); + } + + @Override + public void onCreate() { + super.onCreate(); + + context = getApplicationContext(); + + try { + eipDefinition = ConfigHelper.getJsonFromSharedPref(ConfigHelper.EIP_SERVICE_KEY); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + this.retreiveVpnService(); + } + + @Override + public void onDestroy() { + unbindService(mVpnServiceConn); + mBound = false; + + super.onDestroy(); + } + + @Override + protected void onHandleIntent(Intent intent) { + String action = intent.getAction(); + mReceiver = intent.getParcelableExtra(ConfigHelper.RECEIVER_TAG); + + if ( action == ACTION_IS_EIP_RUNNING ) + this.isRunning(); + if ( action == ACTION_UPDATE_EIP_SERVICE ) + this.updateEIPService(); + else if ( action == ACTION_START_EIP ) + this.startEIP(); + else if ( action == ACTION_STOP_EIP ) + this.stopEIP(); + } + + /** + * Sends an Intent to bind OpenVpnService. + * Used when OpenVpnService isn't bound but might be running. + */ + private void retreiveVpnService() { + Intent bindIntent = new Intent(this,OpenVpnService.class); + bindIntent.setAction(OpenVpnService.RETRIEVE_SERVICE); + bindService(bindIntent, mVpnServiceConn, 0); + } + + private static ServiceConnection mVpnServiceConn = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + LocalBinder binder = (LocalBinder) service; + mVpnService = binder.getService(); + mBound = true; + + if (mReceiver != null && mPending != null) { + + boolean running = mVpnService.isRunning(); + int resultCode = Activity.RESULT_CANCELED; + + if (mPending.equals(ACTION_IS_EIP_RUNNING)) + resultCode = (running) ? Activity.RESULT_OK : Activity.RESULT_CANCELED; + if (mPending.equals(ACTION_START_EIP)) + resultCode = (running) ? Activity.RESULT_OK : Activity.RESULT_CANCELED; + else if (mPending.equals(ACTION_STOP_EIP)) + resultCode = (running) ? Activity.RESULT_CANCELED + : Activity.RESULT_OK; + Bundle resultData = new Bundle(); + resultData.putString(ConfigHelper.REQUEST_TAG, EIP_NOTIFICATION); + mReceiver.send(resultCode, resultData); + + mPending = null; + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mBound = false; + + if (mReceiver != null){ + Bundle resultData = new Bundle(); + resultData.putString(ConfigHelper.REQUEST_TAG, EIP_NOTIFICATION); + mReceiver.send(Activity.RESULT_CANCELED, resultData); + } + } + + }; + + /** + * Attempts to determine if OpenVpnService has an established VPN connection + * through the bound ServiceConnection. If there is no bound service, this + * method will attempt to bind a running OpenVpnService and send + * <code>Activity.RESULT_CANCELED</code> to the ResultReceiver that made the + * request. + * Note: If the request to bind OpenVpnService is successful, the ResultReceiver + * will be notified in {@link onServiceConnected()} + */ + private void isRunning() { + Bundle resultData = new Bundle(); + resultData.putString(ConfigHelper.REQUEST_TAG, ACTION_IS_EIP_RUNNING); + int resultCode = Activity.RESULT_CANCELED; + if (mBound) { + resultCode = (mVpnService.isRunning()) ? Activity.RESULT_OK : Activity.RESULT_CANCELED; + } else { + mPending = ACTION_IS_EIP_RUNNING; + this.retreiveVpnService(); + } + + if (mReceiver != null){ + mReceiver.send(resultCode, resultData); + } + } + + /** + * Initiates an EIP connection by selecting a gateway and preparing and sending an + * Intent to {@link se.leap.openvpn.LaunchVPN} + */ + private void startEIP() { + if (activeGateway==null) + activeGateway = selectGateway(); + + Intent intent = new Intent(this,LaunchVPN.class); + intent.setAction(Intent.ACTION_MAIN); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(LaunchVPN.EXTRA_KEY, activeGateway.mVpnProfile.getUUID().toString() ); + intent.putExtra(LaunchVPN.EXTRA_NAME, activeGateway.mVpnProfile.getName() ); + intent.putExtra(ConfigHelper.RECEIVER_TAG, mReceiver); + startActivity(intent); + mPending = ACTION_START_EIP; + } + + /** + * Disconnects the EIP connection gracefully through the bound service or forcefully + * if there is no bound service. Sends a message to the requesting ResultReceiver. + */ + private void stopEIP() { + if (mBound) + mVpnService.onRevoke(); + else + OpenVpnManagementThread.stopOpenVPN(); + + if (mReceiver != null){ + Bundle resultData = new Bundle(); + resultData.putString(ConfigHelper.REQUEST_TAG, ACTION_STOP_EIP); + mReceiver.send(Activity.RESULT_OK, resultData); + } + } + + /** + * Loads eip-service.json from SharedPreferences and calls {@link updateGateways()} + * to parse gateway definitions. + * TODO Implement API call to refresh eip-service.json from the provider + */ + private void updateEIPService() { + try { + eipDefinition = ConfigHelper.getJsonFromSharedPref(ConfigHelper.EIP_SERVICE_KEY); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + updateGateways(); + } + + /** + * Choose a gateway to connect to based on timezone from system locale data + * + * @return The gateway to connect to + */ + private OVPNGateway selectGateway() { + // TODO Implement gateway selection logic based on TZ or preferences + // TODO Implement search through gateways loaded from SharedPreferences + // TODO Remove String arg constructor in favor of findGatewayByName(String) + return new OVPNGateway("first"); + } + + /** + * Walk the list of gateways defined in eip-service.json and parse them into + * OVPNGateway objects. + * TODO Store the OVPNGateways (as Serializable) in SharedPreferences + */ + private void updateGateways(){ + JSONArray gatewaysDefined = null; + + try { + gatewaysDefined = eipDefinition.getJSONArray("gateways"); + } catch (JSONException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + for ( int i=0 ; i < gatewaysDefined.length(); i++ ){ + + JSONObject gw = null; + + try { + gw = gatewaysDefined.getJSONObject(i); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + if ( gw.getJSONObject("capabilities").getJSONArray("transport").toString().contains("openvpn") ){ + new OVPNGateway(gw); + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + /** + * OVPNGateway provides objects defining gateways and their options and metadata. + * Each instance contains a VpnProfile for OpenVPN specific data and member + * variables describing capabilities and location + * + * @author Sean Leonard <meanderingcode@aetherislands.net> + */ + private class OVPNGateway { + + private String TAG = "OVPNGateway"; + + private VpnProfile mVpnProfile; + private JSONObject mGateway; + private HashMap<String,Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>(); + + + /** + * Attempts to retrieve a VpnProfile by name and build an OVPNGateway around it. + * FIXME This needs to become a findGatewayByName() method + * + * @param name The hostname of the gateway to inflate + */ + private OVPNGateway(String name){ + ProfileManager vpl = ProfileManager.getInstance(context); + + try { + + if ( name == "first" ) { + name = vpl.getProfiles().iterator().next().mName; + } + + mVpnProfile = vpl.getProfileByName(name); + + } catch (NoSuchElementException e) { + updateEIPService(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json + * and create a VpnProfile belonging to it. + * + * @param gateway The JSON OpenVPN gateway definition to parse + */ + protected OVPNGateway(JSONObject gateway){ + + mGateway = gateway; + + // Currently deletes VpnProfile for host, if there already is one, and builds new + ProfileManager vpl = ProfileManager.getInstance(context); + Collection<VpnProfile> profiles = vpl.getProfiles(); + for (VpnProfile p : profiles){ + try { + if ( p.mName.contains( gateway.getString("host") ) ) + vpl.removeProfile(context, p); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + this.parseOptions(); + this.createVPNProfile(); + + setUniqueProfileName(vpl); + vpl.addProfile(mVpnProfile); + vpl.saveProfile(context, mVpnProfile); + vpl.saveProfileList(context); + } + + /** + * Attempts to create a unique profile name from the hostname of the gateway + * + * @param profileManager + */ + private void setUniqueProfileName(ProfileManager profileManager) { + int i=0; + + String newname; + try { + newname = mGateway.getString("host"); + while(profileManager.getProfileByName(newname)!=null) { + i++; + if(i==1) + newname = getString(R.string.converted_profile); + else + newname = getString(R.string.converted_profile_i,i); + } + + mVpnProfile.mName=newname; + } catch (JSONException e) { + // TODO Auto-generated catch block + Log.v(TAG,"Couldn't read gateway name for profile creation!"); + e.printStackTrace(); + } + } + + /** + * FIXME This method is really the outline of the refactoring needed in se.leap.openvpn.ConfigParser + */ + private void parseOptions(){ + + // FIXME move these to a common API (& version) definition place, like ProviderAPI or ConfigHelper + String common_options = "openvpn_configuration"; + String remote = "ip_address"; + String ports = "ports"; + String protos = "protocols"; + String capabilities = "capabilities"; + + Vector<String> arg = new Vector<String>(); + Vector<Vector<String>> args = new Vector<Vector<String>>(); + + try { + JSONObject def = (JSONObject) eipDefinition.get(common_options); + Iterator keys = def.keys(); + Vector<Vector<String>> value = new Vector<Vector<String>>(); + while ( keys.hasNext() ){ + String key = keys.next().toString(); + + arg.add(key); + for ( String word : def.getString(key).split(" ") ) + arg.add(word); + value.add( (Vector<String>) arg.clone() ); + options.put(key, (Vector<Vector<String>>) value.clone()); + + value.clear(); + arg.clear(); + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + arg.add("remote"); + arg.add(mGateway.getString(remote)); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + args.add((Vector<String>) arg.clone()); + options.put("remote", (Vector<Vector<String>>) args.clone() ); + arg.clear(); + args.clear(); + + JSONArray protocolsJSON = null; + arg.add("proto"); + try { + protocolsJSON = mGateway.getJSONObject(capabilities).getJSONArray(protos); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Vector<String> protocols = new Vector<String>(); + for ( int i=0; i<protocolsJSON.length(); i++ ) + protocols.add(protocolsJSON.optString(i)); + if ( protocols.contains("udp")) + arg.add("udp"); + else if ( protocols.contains("tcp")) + arg.add("tcp"); + args.add((Vector<String>) arg.clone()); + options.put("proto", (Vector<Vector<String>>) args.clone()); + arg.clear(); + args.clear(); + + + String port = null; + arg.add("port"); + try { + port = mGateway.getJSONObject(capabilities).getJSONArray(ports).optString(0); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + arg.add(port); + args.add((Vector<String>) arg.clone()); + options.put("port", (Vector<Vector<String>>) args.clone()); + args.clear(); + arg.clear(); + } + + /** + * Create and attach the VpnProfile to our gateway object + */ + protected void createVPNProfile(){ + try { + ConfigParser cp = new ConfigParser(); + cp.setDefinition(options); + VpnProfile vp = cp.convertProfile(); + mVpnProfile = vp; + Log.v(TAG,"Created VPNProfile"); + } catch (ConfigParseError e) { + // FIXME We didn't get a VpnProfile! Error handling! and log level + Log.v(TAG,"Error createing VPNProfile"); + e.printStackTrace(); + } + } + } + +} diff --git a/src/se/leap/leapclient/Provider.java b/src/se/leap/leapclient/Provider.java index 50bd2925..189c3a84 100644 --- a/src/se/leap/leapclient/Provider.java +++ b/src/se/leap/leapclient/Provider.java @@ -122,8 +122,9 @@ public final class Provider implements Serializable { } catch (Exception e) { // TODO: handle exception } - for (int i=0;i<API_EIP_TYPES.length;i++){ + for (int i=0;i<API_EIP_TYPES.length+1;i++){ try { + // Walk the EIP types array looking for matches in provider's service definitions if ( Arrays.asList(API_EIP_TYPES).contains( services.getString(i) ) ) return true; } catch (JSONException e) { diff --git a/src/se/leap/leapclient/ProviderAPI.java b/src/se/leap/leapclient/ProviderAPI.java index 00d7d820..72cc9b62 100644 --- a/src/se/leap/leapclient/ProviderAPI.java +++ b/src/se/leap/leapclient/ProviderAPI.java @@ -584,8 +584,18 @@ public class ProviderAPI extends IntentService { boolean danger_on = ConfigHelper.getBoolFromSharedPref(ConfigHelper.DANGER_ON); String cert_string = getStringFromProvider(new_cert_string_url, danger_on); - if(!cert_string.isEmpty()) { - ConfigHelper.saveSharedPref(ConfigHelper.CERT_KEY, cert_string); + if(!cert_string.isEmpty()) { + // API returns concatenated cert & key. Split them for OpenVPN options + String certificate = null, key = null; + String[] certAndKey = cert_string.split("(?<=-\n)"); + for (int i=0; i < certAndKey.length-1; i++){ + if ( certAndKey[i].contains("KEY") ) + key = certAndKey[i++] + certAndKey[i]; + else if ( certAndKey[i].contains("CERTIFICATE") ) + certificate = certAndKey[i++] + certAndKey[i]; + } + ConfigHelper.saveSharedPref(ConfigHelper.CERT_KEY, certificate); + ConfigHelper.saveSharedPref(ConfigHelper.KEY_KEY, key); return true; } else { return false; |