diff options
Diffstat (limited to 'app/src/main/java')
17 files changed, 725 insertions, 420 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java index 7b2accd6..a52df460 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java @@ -72,9 +72,9 @@ public class ConfigHelper { "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3"; final public static BigInteger G = new BigInteger("2"); - public static boolean checkErroneousDownload(String downloaded_string) { + public static boolean checkErroneousDownload(String downloadedString) { try { - if (downloaded_string == null || downloaded_string.isEmpty() || new JSONObject(downloaded_string).has(ProviderAPI.ERRORS)) { + if (downloadedString == null || downloadedString.isEmpty() || new JSONObject(downloadedString).has(ProviderAPI.ERRORS)) { return true; } else { return false; @@ -158,7 +158,7 @@ public class ConfigHelper { } protected static RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) { - RSAPrivateKey key = null; + RSAPrivateKey key; try { KeyFactory kf = KeyFactory.getInstance("RSA", "BC"); rsaKeyString = rsaKeyString.replaceFirst("-----BEGIN RSA PRIVATE KEY-----", "").replaceFirst("-----END RSA PRIVATE KEY-----", ""); @@ -282,6 +282,7 @@ public class ConfigHelper { provider.setCaCert(preferences.getString(Provider.CA_CERT, "")); provider.setVpnCertificate(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); provider.setPrivateKey(preferences.getString(PROVIDER_PRIVATE_KEY, "")); + provider.setEipServiceJson(new JSONObject(preferences.getString(PROVIDER_EIP_DEFINITION, ""))); } catch (MalformedURLException | JSONException e) { e.printStackTrace(); } @@ -374,8 +375,6 @@ public class ConfigHelper { putString(Provider.KEY + "." + providerDomain, provider.getDefinitionString()). putString(Provider.CA_CERT + "." + providerDomain, provider.getCaCert()). putString(PROVIDER_EIP_DEFINITION + "." + providerDomain, provider.getEipServiceJsonString()). - putString(PROVIDER_PRIVATE_KEY + "." + providerDomain, provider.getPrivateKey()). - putString(PROVIDER_VPN_CERTIFICATE + "." + providerDomain, provider.getVpnCertificate()). apply(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java index 7fcb5816..ea328216 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java @@ -2,6 +2,7 @@ package se.leap.bitmaskclient; import android.content.SharedPreferences; import android.os.Bundle; +import android.os.PersistableBundle; import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; import android.support.annotation.StringRes; @@ -74,6 +75,20 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { setProviderHeaderText(provider.getName()); } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if (provider != null) { + outState.putParcelable(PROVIDER_KEY, provider); + } + } + + protected void restoreState(Bundle savedInstanceState) { + if (savedInstanceState != null && savedInstanceState.containsKey(PROVIDER_KEY)) { + provider = savedInstanceState.getParcelable(PROVIDER_KEY); + } + } + protected void setProviderHeaderLogo(@DrawableRes int providerHeaderLogo) { this.providerHeaderLogo.setImageResource(providerHeaderLogo); } diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index fb2655e3..2b7a8113 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -35,13 +35,11 @@ public interface Constants { String EIP_ACTION_CHECK_CERT_VALIDITY = "EIP.CHECK_CERT_VALIDITY"; String EIP_ACTION_START = "se.leap.bitmaskclient.EIP.START"; String EIP_ACTION_STOP = "se.leap.bitmaskclient.EIP.STOP"; - String EIP_ACTION_UPDATE = "se.leap.bitmaskclient.EIP.UPDATE"; String EIP_ACTION_IS_RUNNING = "se.leap.bitmaskclient.EIP.IS_RUNNING"; String EIP_ACTION_START_ALWAYS_ON_VPN = "se.leap.bitmaskclient.START_ALWAYS_ON_VPN"; String EIP_ACTION_START_BLOCKING_VPN = "se.leap.bitmaskclient.EIP_ACTION_START_BLOCKING_VPN"; String EIP_ACTION_STOP_BLOCKING_VPN = "se.leap.bitmaskclient.EIP_ACTION_STOP_BLOCKING_VPN"; - String EIP_NOTIFICATION = "EIP.NOTIFICATION"; String EIP_RECEIVER = "EIP.RECEIVER"; String EIP_REQUEST = "EIP.REQUEST"; String EIP_RESTART_ON_BOOT = "EIP.RESTART_ON_BOOT"; diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index 4bacfff8..fb57aea8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -17,23 +17,19 @@ package se.leap.bitmaskclient; import android.app.Activity; -import android.app.AlertDialog; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.os.Bundle; import android.os.IBinder; -import android.os.RemoteException; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; -import android.support.v4.content.LocalBroadcastManager; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.AppCompatImageView; import android.util.Log; import android.view.LayoutInflater; @@ -50,45 +46,27 @@ import butterknife.InjectView; import butterknife.OnClick; import de.blinkt.openvpn.core.IOpenVPNServiceInternal; import de.blinkt.openvpn.core.OpenVPNService; -import de.blinkt.openvpn.core.ProfileManager; -import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.eip.EipCommand; import se.leap.bitmaskclient.eip.EipStatus; import se.leap.bitmaskclient.eip.VoidVpnService; import se.leap.bitmaskclient.views.VpnStateImage; -import static android.app.Activity.RESULT_OK; -import static android.content.Intent.CATEGORY_DEFAULT; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK; -import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT; -import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT; -import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; -import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_UPDATE; -import static se.leap.bitmaskclient.Constants.EIP_NOTIFICATION; -import static se.leap.bitmaskclient.Constants.EIP_REQUEST; import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_LOG_IN; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; -import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; -import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderCredentialsBaseActivity.USER_MESSAGE; +import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message; public class EipFragment extends Fragment implements Observer { public final static String TAG = EipFragment.class.getSimpleName(); - protected static final String IS_CONNECTED = TAG + ".is_connected"; public static final String START_EIP_ON_BOOT = "start on boot"; public static final String ASK_TO_CANCEL_VPN = "ask_to_cancel_vpn"; @@ -112,9 +90,14 @@ public class EipFragment extends Fragment implements Observer { TextView vpnRoute; private EipStatus eipStatus; - private boolean wantsToConnect; - private EIPFragmentBroadcastReceiver eipFragmentBroadcastReceiver; + //---saved Instance ------- + private final static String KEY_SHOW_PENDING_START_CANCELLATION = "KEY_SHOW_PENDING_START_CANCELLATION"; + private final static String KEY_SHOW_ASK_TO_STOP_EIP = "KEY_SHOW_ASK_TO_STOP_EIP"; + private boolean showPendingStartCancellation = false; + private boolean showAskToStopEip = false; + //------------------------ + AlertDialog alertDialog; private IOpenVPNServiceInternal mService; private ServiceConnection openVpnConnection = new ServiceConnection() { @@ -159,7 +142,6 @@ public class EipFragment extends Fragment implements Observer { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); eipStatus = EipStatus.getInstance(); - eipFragmentBroadcastReceiver = new EIPFragmentBroadcastReceiver(); Activity activity = getActivity(); if (activity != null) { preferences = getActivity().getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); @@ -176,8 +158,11 @@ public class EipFragment extends Fragment implements Observer { Bundle arguments = getArguments(); if (arguments != null && arguments.containsKey(ASK_TO_CANCEL_VPN) && arguments.getBoolean(ASK_TO_CANCEL_VPN)) { + arguments.remove(ASK_TO_CANCEL_VPN); + setArguments(arguments); askToStopEIP(); } + restoreFromSavedInstance(savedInstanceState); return view; } @@ -186,7 +171,6 @@ public class EipFragment extends Fragment implements Observer { super.onResume(); //FIXME: avoid race conditions while checking certificate an logging in at about the same time //eipCommand(Constants.EIP_ACTION_CHECK_CERT_VALIDITY); - setUpBroadcastReceiver(); handleNewState(); bindOpenVpnService(); } @@ -198,21 +182,37 @@ public class EipFragment extends Fragment implements Observer { Activity activity = getActivity(); if (activity != null) { getActivity().unbindService(openVpnConnection); - LocalBroadcastManager.getInstance(activity).unregisterReceiver(eipFragmentBroadcastReceiver); } Log.d(TAG, "broadcast unregistered"); } @Override - public void onDestroyView() { - super.onDestroyView(); - eipStatus.deleteObserver(this); + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + if (showAskToStopEip) { + outState.putBoolean(KEY_SHOW_ASK_TO_STOP_EIP, true); + alertDialog.dismiss(); + } else if (showPendingStartCancellation) { + outState.putBoolean(KEY_SHOW_PENDING_START_CANCELLATION, true); + alertDialog.dismiss(); + + } + } + + private void restoreFromSavedInstance(Bundle savedInstanceState) { + if (savedInstanceState != null && savedInstanceState.containsKey(KEY_SHOW_PENDING_START_CANCELLATION)) { + showPendingStartCancellation = true; + askPendingStartCancellation(); + } else if (savedInstanceState != null && savedInstanceState.containsKey(KEY_SHOW_ASK_TO_STOP_EIP)) { + showAskToStopEip = true; + askToStopEIP(); + } } @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - outState.putBoolean(IS_CONNECTED, eipStatus.isConnected()); - super.onSaveInstanceState(outState); + public void onDestroyView() { + super.onDestroyView(); + eipStatus.deleteObserver(this); } private void saveStatus(boolean restartOnBoot) { @@ -246,13 +246,7 @@ public class EipFragment extends Fragment implements Observer { if (canStartEIP()) { startEipFromScratch(); } else if (canLogInToStartEIP()) { - wantsToConnect = true; - Intent intent = new Intent(getContext(), LoginActivity.class); - intent.putExtra(PROVIDER_KEY, provider); - Activity activity = getActivity(); - if (activity != null) { - activity.startActivityForResult(intent, REQUEST_CODE_LOG_IN); - } + askUserToLogIn(getString(vpn_certificate_user_message)); } else { // provider has no VpnCertificate but user is logged in downloadVpnCertificate(); @@ -267,7 +261,7 @@ public class EipFragment extends Fragment implements Observer { private boolean canLogInToStartEIP() { boolean isAllowedRegistered = provider.allowsRegistered(); - boolean isLoggedIn = !LeapSRPSession.getToken().isEmpty(); + boolean isLoggedIn = LeapSRPSession.loggedIn(); return isAllowedRegistered && !isLoggedIn && !eipStatus.isConnecting() && !eipStatus.isConnected(); } @@ -279,38 +273,13 @@ public class EipFragment extends Fragment implements Observer { } } - private void askPendingStartCancellation() { - Activity activity = getActivity(); - if (activity == null) { - Log.e(TAG, "activity is null when asking to cancel"); - return; - } - AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity()); - alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title)) - .setMessage(activity.getString(R.string.eip_cancel_connect_text)) - .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - stopEipIfPossible(); - } - }) - .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - }) - .show(); - - } - public void startEipFromScratch() { + saveStatus(true); Context context = getContext(); if (context == null) { Log.e(TAG, "context is null when trying to start VPN"); return; } - wantsToConnect = false; - saveStatus(true); EipCommand.startVPN(context, false); vpnStateImage.showProgress(); routedText.setVisibility(GONE); @@ -318,54 +287,54 @@ public class EipFragment extends Fragment implements Observer { colorBackgroundALittle(); } - private void stop() { - saveStatus(false); - if (eipStatus.isBlockingVpnEstablished()) { - stopBlockingVpn(); + protected void stopEipIfPossible() { + Context context = getContext(); + if (context != null) { + EipCommand.stopVPN(getContext()); + } else { + Log.e(TAG, "context is null when trying to stop EIP"); } - disconnect(); } - private void stopBlockingVpn() { - Log.d(TAG, "stop VoidVpn!"); + private void askPendingStartCancellation() { Activity activity = getActivity(); if (activity == null) { - // TODO what to do if not stopping void vpn? - Log.e(TAG, "activity is null when trying to stop blocking vpn"); + Log.e(TAG, "activity is null when asking to cancel"); return; } - Intent stopVoidVpnIntent = new Intent(activity, VoidVpnService.class); - stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); - activity.startService(stopVoidVpnIntent); - } - private void disconnect() { - ProfileManager.setConntectedVpnProfileDisconnected(getActivity()); - if (mService != null) { - try { - mService.stopVPN(false); - } catch (RemoteException e) { - VpnStatus.logException(e); + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity()); + showPendingStartCancellation = true; + alertDialog = alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title)) + .setMessage(activity.getString(R.string.eip_cancel_connect_text)) + .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + stopEipIfPossible(); + } + }) + .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }).setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + showPendingStartCancellation = false; } - } - } + }).show(); - protected void stopEipIfPossible() { - Context context = getContext(); - if (context == null) { - Log.e(TAG, "context is null when trying to stop EIP"); - return; - } - EipCommand.stopVPN(getContext()); } protected void askToStopEIP() { Activity activity = getActivity(); if (activity == null) { Log.e(TAG, "activity is null when asking to stop EIP"); + return; } AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity); - alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title)) + showAskToStopEip = true; + alertDialog = alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title)) .setMessage(activity.getString(R.string.eip_warning_browser_inconsistency)) .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() { @Override @@ -377,8 +346,12 @@ public class EipFragment extends Fragment implements Observer { @Override public void onClick(DialogInterface dialog, int which) { } - }) - .show(); + }).setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + showAskToStopEip = false; + } + }).show(); } @Override @@ -457,84 +430,6 @@ public class EipFragment extends Fragment implements Observer { } - private class EIPFragmentBroadcastReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "received Broadcast"); - - String action = intent.getAction(); - if (action == null) { - return; - } - - int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, -1); - Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); - switch (action) { - case BROADCAST_EIP_EVENT: - handleEIPEvent(resultCode, resultData); - break; - case BROADCAST_PROVIDER_API_EVENT: - handleProviderApiEvent(resultCode, resultData); - break; - } - } - } - - private void handleEIPEvent(int resultCode, Bundle resultData) { - String request = resultData.getString(EIP_REQUEST); - - if (request == null) { - return; - } - - switch (request) { - case EIP_ACTION_START: - switch (resultCode) { - case RESULT_OK: - break; - case Activity.RESULT_CANCELED: - break; - } - break; - case EIP_ACTION_STOP: - switch (resultCode) { - case RESULT_OK: - stop(); - break; - case Activity.RESULT_CANCELED: - break; - } - break; - case EIP_NOTIFICATION: - switch (resultCode) { - case RESULT_OK: - break; - case Activity.RESULT_CANCELED: - break; - } - break; - case EIP_ACTION_CHECK_CERT_VALIDITY: - switch (resultCode) { - case RESULT_OK: - break; - case Activity.RESULT_CANCELED: - downloadVpnCertificate(); - break; - } - break; - case EIP_ACTION_UPDATE: - switch (resultCode) { - case RESULT_OK: - if (wantsToConnect) - startEipFromScratch(); - break; - case Activity.RESULT_CANCELED: - handleNewState(); - break; - } - } - } - private void greyscaleBackground() { ColorMatrix matrix = new ColorMatrix(); matrix.setSaturation(0); @@ -553,46 +448,21 @@ public class EipFragment extends Fragment implements Observer { background.setImageAlpha(210); } - public void handleProviderApiEvent(int resultCode, Bundle resultData) { - Context context = getContext(); - if (context == null) { - return; - } - - // TODO call DOWNLOAD_EIP_SERVICES ore remove respective cases - switch (resultCode) { - case CORRECTLY_DOWNLOADED_EIP_SERVICE: - provider = resultData.getParcelable(PROVIDER_KEY); - EipCommand.updateEipService(context); - break; - case INCORRECTLY_DOWNLOADED_EIP_SERVICE: - //dashboard.setResult(RESULT_CANCELED); - // TODO CATCH ME IF YOU CAN - WHAT DO WE WANT TO DO? - break; - case CORRECTLY_DOWNLOADED_CERTIFICATE: - startEipFromScratch(); - break; - case INCORRECTLY_DOWNLOADED_CERTIFICATE: - // TODO CATCH ME IF YOU CAN - LOGIN? - break; - } - } - private void downloadVpnCertificate() { - ProviderAPICommand.execute(getContext(), DOWNLOAD_CERTIFICATE, provider); + ProviderAPICommand.execute(getContext(), DOWNLOAD_VPN_CERTIFICATE, provider); } - private void setUpBroadcastReceiver() { + private void askUserToLogIn(String userMessage) { + Intent intent = new Intent(getContext(), LoginActivity.class); + intent.putExtra(PROVIDER_KEY, provider); + + if(userMessage != null) { + intent.putExtra(USER_MESSAGE, userMessage); + } + Activity activity = getActivity(); if (activity != null) { - IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_EIP_EVENT); - updateIntentFilter.addAction(BROADCAST_PROVIDER_API_EVENT); - updateIntentFilter.addCategory(CATEGORY_DEFAULT); - LocalBroadcastManager.getInstance(activity).registerReceiver(eipFragmentBroadcastReceiver, updateIntentFilter); - Log.d(TAG, "broadcast registered"); - } else { - Log.e(TAG, "activity null when setting up broadcast receiver"); + activity.startActivityForResult(intent, REQUEST_CODE_LOG_IN); } } - } diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java index 83ab4144..6e778309 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -1,33 +1,97 @@ package se.leap.bitmaskclient; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.SharedPreferences; import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.content.LocalBroadcastManager; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; +import android.util.Log; +import org.jetbrains.annotations.NotNull; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Observable; +import java.util.Observer; + +import de.blinkt.openvpn.core.IOpenVPNServiceInternal; +import de.blinkt.openvpn.core.OpenVPNService; +import de.blinkt.openvpn.core.ProfileManager; +import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.drawer.NavigationDrawerFragment; import se.leap.bitmaskclient.eip.EipCommand; +import se.leap.bitmaskclient.eip.EipStatus; +import se.leap.bitmaskclient.eip.VoidVpnService; import se.leap.bitmaskclient.fragments.LogFragment; +import static android.content.Intent.CATEGORY_DEFAULT; +import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT; +import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN; +import static se.leap.bitmaskclient.Constants.EIP_REQUEST; +import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_LOG_IN; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.EipFragment.ASK_TO_CANCEL_VPN; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.ERRORS; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderCredentialsBaseActivity.USER_MESSAGE; +import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed; +import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message; + +public class MainActivity extends AppCompatActivity implements Observer { -public class MainActivity extends AppCompatActivity { + public final static String TAG = MainActivity.class.getSimpleName(); + + private static final String KEY_ACTIVITY_STATE = "key state of activity"; + private static final String DEFAULT_UI_STATE = "default state"; + private static final String SHOW_DIALOG_STATE = "show dialog"; + private static final String REASON_TO_FAIL = "reason to fail"; private static Provider provider = new Provider(); - private static FragmentManagerEnhanced fragmentManager; private SharedPreferences preferences; - + private EipStatus eipStatus; private NavigationDrawerFragment navigationDrawerFragment; + private MainActivityBroadcastReceiver mainActivityBroadcastReceiver; + + private IOpenVPNServiceInternal mService; + private ServiceConnection openVpnConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + mService = IOpenVPNServiceInternal.Stub.asInterface(service); + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + mService = null; + } + + }; public final static String ACTION_SHOW_VPN_FRAGMENT = "action_show_vpn_fragment"; public final static String ACTION_SHOW_LOG_FRAGMENT = "action_show_log_fragment"; @@ -42,22 +106,31 @@ public class MainActivity extends AppCompatActivity { setContentView(R.layout.activity_main); setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + mainActivityBroadcastReceiver = new MainActivityBroadcastReceiver(); + setUpBroadcastReceiver(); + navigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); provider = ConfigHelper.getSavedProviderFromSharedPreferences(preferences); - fragmentManager = new FragmentManagerEnhanced(getSupportFragmentManager()); // Set up the drawer. navigationDrawerFragment.setUp( R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout)); + eipStatus = EipStatus.getInstance(); handleIntentAction(getIntent()); } @Override + protected void onResume() { + super.onResume(); + bindOpenVpnService(); + } + + @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); @@ -86,6 +159,9 @@ public class MainActivity extends AppCompatActivity { default: break; } + // on layout change / recreation of the activity, we don't want create new Fragments + // instead the fragments themselves care about recreation and state restoration + intent.setAction(null); if (fragment != null) { new FragmentManagerEnhanced(getSupportFragmentManager()).beginTransaction() @@ -127,8 +203,191 @@ public class MainActivity extends AppCompatActivity { Bundle arguments = new Bundle(); arguments.putParcelable(PROVIDER_KEY, provider); fragment.setArguments(arguments); - fragmentManager.beginTransaction() + new FragmentManagerEnhanced(getSupportFragmentManager()).beginTransaction() .replace(R.id.container, fragment) .commit(); } + + @Override + protected void onPause() { + super.onPause(); + unbindService(openVpnConnection); + } + + @Override + protected void onDestroy() { + LocalBroadcastManager.getInstance(this).unregisterReceiver(mainActivityBroadcastReceiver); + mainActivityBroadcastReceiver = null; + super.onDestroy(); + } + + + @Override + public void update(Observable observable, Object data) { + if (observable instanceof EipStatus) { + eipStatus = (EipStatus) observable; + } + } + + private void setUpBroadcastReceiver() { + IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_EIP_EVENT); + updateIntentFilter.addAction(BROADCAST_PROVIDER_API_EVENT); + updateIntentFilter.addCategory(CATEGORY_DEFAULT); + LocalBroadcastManager.getInstance(this).registerReceiver(mainActivityBroadcastReceiver, updateIntentFilter); + Log.d(TAG, "broadcast registered"); + } + + private class MainActivityBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "received Broadcast"); + + String action = intent.getAction(); + if (action == null) { + return; + } + + int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED); + Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); + if (resultData == null) { + resultData = Bundle.EMPTY; + } + + switch (action) { + case BROADCAST_EIP_EVENT: + handleEIPEvent(resultCode, resultData); + break; + case BROADCAST_PROVIDER_API_EVENT: + handleProviderApiEvent(resultCode, resultData); + break; + } + } + } + + private void handleEIPEvent(int resultCode, Bundle resultData) { + String request = resultData.getString(EIP_REQUEST); + + if (request == null) { + return; + } + + switch (request) { + case EIP_ACTION_START: + switch (resultCode) { + case RESULT_OK: + break; + case RESULT_CANCELED: + String error = resultData.getString(ERRORS); + if (LeapSRPSession.loggedIn() || provider.allowsAnonymous()) { + showMainActivityErrorDialog(error); + } else { + askUserToLogIn(getString(vpn_certificate_user_message)); + } + break; + } + break; + case EIP_ACTION_STOP: + switch (resultCode) { + case RESULT_OK: + stop(); + break; + case RESULT_CANCELED: + break; + } + break; + } + } + + public void handleProviderApiEvent(int resultCode, Bundle resultData) { + // TODO call DOWNLOAD_EIP_SERVICES ore remove respective cases + switch (resultCode) { + case CORRECTLY_DOWNLOADED_EIP_SERVICE: + provider = resultData.getParcelable(PROVIDER_KEY); + EipCommand.startVPN(this, true); + break; + case INCORRECTLY_DOWNLOADED_EIP_SERVICE: + // TODO CATCH ME IF YOU CAN - WHAT DO WE WANT TO DO? + break; + + case CORRECTLY_DOWNLOADED_VPN_CERTIFICATE: + provider = resultData.getParcelable(PROVIDER_KEY); + ConfigHelper.storeProviderInPreferences(preferences, provider); + EipCommand.startVPN(this, true); + break; + case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE: + if (LeapSRPSession.loggedIn() || provider.allowsAnonymous()) { + showMainActivityErrorDialog(getString(downloading_vpn_certificate_failed)); + } else { + askUserToLogIn(getString(vpn_certificate_user_message)); + } + break; + } + } + + /** + * Shows an error dialog + */ + public void showMainActivityErrorDialog(String reasonToFail) { + try { + + FragmentTransaction fragmentTransaction = new FragmentManagerEnhanced( + this.getSupportFragmentManager()).removePreviousFragment( + MainActivityErrorDialog.TAG); + DialogFragment newFragment; + try { + JSONObject errorJson = new JSONObject(reasonToFail); + newFragment = MainActivityErrorDialog.newInstance(provider, errorJson); + } catch (JSONException e) { + e.printStackTrace(); + newFragment = MainActivityErrorDialog.newInstance(provider, reasonToFail); + } + newFragment.show(fragmentTransaction, MainActivityErrorDialog.TAG); + } catch (IllegalStateException | NullPointerException e) { + e.printStackTrace(); + Log.w(TAG, "error dialog leaked!"); + } + + } + + + private void stop() { + preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply(); + if (eipStatus.isBlockingVpnEstablished()) { + stopBlockingVpn(); + } + disconnect(); + } + + private void stopBlockingVpn() { + Log.d(TAG, "stop VoidVpn!"); + Intent stopVoidVpnIntent = new Intent(this, VoidVpnService.class); + stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); + startService(stopVoidVpnIntent); + } + + private void disconnect() { + ProfileManager.setConntectedVpnProfileDisconnected(this); + if (mService != null) { + try { + mService.stopVPN(false); + } catch (RemoteException e) { + VpnStatus.logException(e); + } + } + } + + private void bindOpenVpnService() { + Intent intent = new Intent(this, OpenVPNService.class); + intent.setAction(OpenVPNService.START_SERVICE); + bindService(intent, openVpnConnection, Context.BIND_AUTO_CREATE); + } + + private void askUserToLogIn(String userMessage) { + Intent intent = new Intent(this, LoginActivity.class); + intent.putExtra(PROVIDER_KEY, provider); + if (userMessage != null) { + intent.putExtra(USER_MESSAGE, userMessage); + } + startActivityForResult(intent, REQUEST_CODE_LOG_IN); + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivityErrorDialog.java b/app/src/main/java/se/leap/bitmaskclient/MainActivityErrorDialog.java new file mode 100644 index 00000000..23bc8427 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivityErrorDialog.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2018 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package se.leap.bitmaskclient; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; + +import org.json.JSONObject; + +import static se.leap.bitmaskclient.MainActivityErrorDialog.DOWNLOAD_ERRORS.DEFAULT; +import static se.leap.bitmaskclient.MainActivityErrorDialog.DOWNLOAD_ERRORS.valueOf; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.eip.EIP.ERRORS; +import static se.leap.bitmaskclient.eip.EIP.ERROR_ID; + +/** + * Implements an error dialog for the main activity. + * + * @author fupduck + * @author cyberta + */ +public class MainActivityErrorDialog extends DialogFragment { + + final public static String TAG = "downloaded_failed_dialog"; + final private static String KEY_REASON_TO_FAIL = "key reason to fail"; + final private static String KEY_PROVIDER = "key provider"; + private String reasonToFail; + private DOWNLOAD_ERRORS downloadError = DEFAULT; + + private Provider provider; + + public enum DOWNLOAD_ERRORS { + DEFAULT, + ERROR_INVALID_VPN_CERTIFICATE, + } + + /** + * @return a new instance of this DialogFragment. + */ + public static DialogFragment newInstance(Provider provider, String reasonToFail) { + MainActivityErrorDialog dialogFragment = new MainActivityErrorDialog(); + dialogFragment.reasonToFail = reasonToFail; + dialogFragment.provider = provider; + return dialogFragment; + } + + /** + * @return a new instance of this DialogFragment. + */ + public static DialogFragment newInstance(Provider provider, JSONObject errorJson) { + MainActivityErrorDialog dialogFragment = new MainActivityErrorDialog(); + dialogFragment.provider = provider; + try { + if (errorJson.has(ERRORS)) { + dialogFragment.reasonToFail = errorJson.getString(ERRORS); + } else { + //default error msg + dialogFragment.reasonToFail = dialogFragment.getString(R.string.error_io_exception_user_message); + } + + if (errorJson.has(ERROR_ID)) { + dialogFragment.downloadError = valueOf(errorJson.getString(ERROR_ID)); + } + } catch (Exception e) { + e.printStackTrace(); + dialogFragment.reasonToFail = dialogFragment.getString(R.string.error_io_exception_user_message); + } + return dialogFragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + restoreFromSavedInstance(savedInstanceState); + } + + @Override + @NonNull + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(reasonToFail) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + } + }); + switch (downloadError) { + case ERROR_INVALID_VPN_CERTIFICATE: + builder.setPositiveButton(R.string.update_certificate, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ProviderAPICommand.execute(getContext(), DOWNLOAD_VPN_CERTIFICATE, provider); + } + }); + break; + default: + break; + } + + // Create the AlertDialog object and return it + return builder.create(); + } + + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(KEY_REASON_TO_FAIL, reasonToFail); + outState.putParcelable(KEY_PROVIDER, provider); + } + + private void restoreFromSavedInstance(Bundle savedInstanceState) { + if (savedInstanceState == null) { + return; + } + if (savedInstanceState.containsKey(KEY_PROVIDER)) { + this.provider = savedInstanceState.getParcelable(KEY_PROVIDER); + } + if (savedInstanceState.containsKey(KEY_REASON_TO_FAIL)) { + this.reasonToFail = savedInstanceState.getString(KEY_REASON_TO_FAIL); + } + } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java index 40b2ea7f..7d1054f1 100644 --- a/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java @@ -52,6 +52,7 @@ import static se.leap.bitmaskclient.R.string.error_io_exception_user_message; import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message; import static se.leap.bitmaskclient.R.string.keyChainAccessError; import static se.leap.bitmaskclient.R.string.server_unreachable_message; +import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert; /** * Created by cyberta on 08.01.18. @@ -90,7 +91,8 @@ public class OkHttpClientGenerator { return clientBuilder.build(); } catch (IllegalArgumentException e) { e.printStackTrace(); - addErrorMessageToJson(initError, resources.getString(R.string.certificate_error)); + // TODO ca cert is invalid - show better error ?! + addErrorMessageToJson(initError, resources.getString(certificate_error)); } catch (IllegalStateException | KeyManagementException | KeyStoreException e) { e.printStackTrace(); addErrorMessageToJson(initError, String.format(resources.getString(keyChainAccessError), e.getLocalizedMessage())); @@ -99,6 +101,7 @@ public class OkHttpClientGenerator { addErrorMessageToJson(initError, resources.getString(error_no_such_algorithm_exception_user_message)); } catch (CertificateException e) { e.printStackTrace(); + // TODO ca cert is invalid - show better error ?! addErrorMessageToJson(initError, resources.getString(certificate_error)); } catch (UnknownHostException e) { e.printStackTrace(); diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java index b3362409..7104143c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Provider.java +++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java @@ -112,10 +112,12 @@ public final class Provider implements Parcelable { public boolean isConfigured() { return !mainUrl.isDefault() && - definition.length() > 0 && !apiUrl.isDefault() && - caCert != null && - !caCert.isEmpty(); + hasCaCert() && + hasDefinition() && + hasVpnCertificate() && + hasEIP() && + hasPrivateKey(); } public void setMainUrl(URL url) { @@ -161,7 +163,7 @@ public final class Provider implements Parcelable { return getDefinition().toString(); } - protected String getDomain() { + public String getDomain() { return mainUrl.getDomain(); } @@ -169,7 +171,7 @@ public final class Provider implements Parcelable { return getMainUrl().toString(); } - protected DefaultedURL getMainUrl() { + public DefaultedURL getMainUrl() { return mainUrl; } @@ -400,6 +402,10 @@ public final class Provider implements Parcelable { this.privateKey = privateKey; } + public boolean hasPrivateKey() { + return privateKey != null && privateKey.length() > 0; + } + public String getVpnCertificate() { return vpnCertificate; } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java index b3399416..2e153c7a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java @@ -44,13 +44,12 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase SIGN_UP = "srpRegister", LOG_IN = "srpAuth", LOG_OUT = "logOut", - DOWNLOAD_CERTIFICATE = "downloadUserAuthedCertificate", + DOWNLOAD_VPN_CERTIFICATE = "downloadUserAuthedVPNCertificate", PARAMETERS = "parameters", RECEIVER_KEY = "receiver", ERRORS = "errors", ERRORID = "errorId", - UPDATE_PROGRESSBAR = "update_progressbar", - DOWNLOAD_EIP_SERVICE = "ProviderAPI.DOWNLOAD_EIP_SERVICE", + DOWNLOAD_SERVICE_JSON = "ProviderAPI.DOWNLOAD_SERVICE_JSON", PROVIDER_SET_UP = "ProviderAPI.PROVIDER_SET_UP"; final public static int @@ -60,8 +59,8 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase FAILED_SIGNUP = 6, SUCCESSFUL_LOGOUT = 7, LOGOUT_FAILED = 8, - CORRECTLY_DOWNLOADED_CERTIFICATE = 9, - INCORRECTLY_DOWNLOADED_CERTIFICATE = 10, + CORRECTLY_DOWNLOADED_VPN_CERTIFICATE = 9, + INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE = 10, PROVIDER_OK = 11, PROVIDER_NOK = 12, CORRECTLY_DOWNLOADED_EIP_SERVICE = 13, @@ -69,8 +68,6 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase ProviderApiManager providerApiManager; - - public ProviderAPI() { super(TAG); } @@ -82,7 +79,6 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase return ProviderApiManager.lastDangerOn(); } - @Override public void onCreate() { super.onCreate(); @@ -99,7 +95,6 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase providerApiManager.handleIntent(command); } - private ProviderApiManager initApiManager() { SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(preferences, getResources()); diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java index 505ee55b..5aff1af1 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java @@ -61,18 +61,18 @@ import static se.leap.bitmaskclient.Constants.CREDENTIALS_USERNAME; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; -import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; -import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON; -import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_INVALID_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; +import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON; +import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_INVALID_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; -import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_SERVICE_JSON; import static se.leap.bitmaskclient.ProviderAPI.ERRORID; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; import static se.leap.bitmaskclient.ProviderAPI.FAILED_LOGIN; import static se.leap.bitmaskclient.ProviderAPI.FAILED_SIGNUP; -import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; import static se.leap.bitmaskclient.ProviderAPI.LOGOUT_FAILED; import static se.leap.bitmaskclient.ProviderAPI.LOG_IN; @@ -89,6 +89,7 @@ import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_LOGOUT; import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_SIGNUP; import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROVIDER_DETAILS; import static se.leap.bitmaskclient.R.string.certificate_error; +import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid; import static se.leap.bitmaskclient.R.string.error_io_exception_user_message; import static se.leap.bitmaskclient.R.string.error_json_exception_user_message; import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message; @@ -184,14 +185,15 @@ public abstract class ProviderApiManagerBase { sendToReceiverOrBroadcast(receiver, LOGOUT_FAILED, Bundle.EMPTY, provider); } break; - case DOWNLOAD_CERTIFICATE: - if (updateVpnCertificate(provider)) { - sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY, provider); + case DOWNLOAD_VPN_CERTIFICATE: + result = updateVpnCertificate(provider); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_VPN_CERTIFICATE, result, provider); } else { - sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY, provider); + sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE, result, provider); } break; - case DOWNLOAD_EIP_SERVICE: + case DOWNLOAD_SERVICE_JSON: result = getAndSetEipServiceJson(provider); if (result.getBoolean(BROADCAST_RESULT_KEY)) { sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_EIP_SERVICE, result, provider); @@ -537,7 +539,7 @@ public abstract class ProviderApiManagerBase { } private String requestStringFromServer(@NonNull String url, @NonNull String request_method, String jsonString, @NonNull List<Pair<String, String>> headerArgs, @NonNull OkHttpClient okHttpClient) { - String plainResponseBody = null; + String plainResponseBody; try { @@ -617,7 +619,7 @@ public abstract class ProviderApiManagerBase { * * @return true if certificate was downloaded correctly, false if provider.json is not present in SharedPreferences, or if the certificate url could not be parsed as a URI, or if there was an SSL error. */ - protected abstract boolean updateVpnCertificate(Provider provider); + protected abstract Bundle updateVpnCertificate(Provider provider); protected boolean isValidJson(String jsonString) { @@ -815,15 +817,17 @@ public abstract class ProviderApiManagerBase { return false; } - protected boolean loadCertificate(Provider provider, String cert_string) { - if (cert_string == null) { - return false; + protected Bundle loadCertificate(Provider provider, String certString) { + Bundle result = new Bundle(); + if (certString == null) { + setErrorResult(result, vpn_certificate_is_invalid, null); + return result; } try { // API returns concatenated cert & key. Split them for OpenVPN options String certificateString = null, keyString = null; - String[] certAndKey = cert_string.split("(?<=-\n)"); + String[] certAndKey = certString.split("(?<=-\n)"); for (int i = 0; i < certAndKey.length - 1; i++) { if (certAndKey[i].contains("KEY")) { keyString = certAndKey[i++] + certAndKey[i]; @@ -837,13 +841,14 @@ public abstract class ProviderApiManagerBase { provider.setPrivateKey( "-----BEGIN RSA PRIVATE KEY-----\n" + keyString + "-----END RSA PRIVATE KEY-----"); X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(certificateString); + certificate.checkValidity(); certificateString = Base64.encodeToString(certificate.getEncoded(), Base64.DEFAULT); provider.setVpnCertificate( "-----BEGIN CERTIFICATE-----\n" + certificateString + "-----END CERTIFICATE-----"); - return true; + result.putBoolean(BROADCAST_RESULT_KEY, true); } catch (CertificateException | NullPointerException e) { - // TODO Auto-generated catch block e.printStackTrace(); - return false; + setErrorResult(result, vpn_certificate_is_invalid, null); } + return result; } } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java index 7714e979..6faf8bb8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java @@ -41,7 +41,7 @@ import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.CREDENTIALS_PASSWORD; import static se.leap.bitmaskclient.Constants.CREDENTIALS_USERNAME; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; -import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.LOG_IN; import static se.leap.bitmaskclient.ProviderAPI.SIGN_UP; @@ -59,7 +59,7 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc final private static String SHOWING_FORM = "SHOWING_FORM"; final private static String PERFORMING_ACTION = "PERFORMING_ACTION"; - final private static String USER_MESSAGE = "USER_MESSAGE"; + final public static String USER_MESSAGE = "USER_MESSAGE"; final private static String USERNAME_ERROR = "USERNAME_ERROR"; final private static String PASSWORD_ERROR = "PASSWORD_ERROR"; final private static String PASSWORD_VERIFICATION_ERROR = "PASSWORD_VERIFICATION_ERROR"; @@ -102,8 +102,12 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc LocalBroadcastManager.getInstance(this).registerReceiver(providerAPIBroadcastReceiver, updateIntentFilter); setUpListeners(); - if(savedInstanceState != null) { - restoreState(savedInstanceState); + restoreState(savedInstanceState); + + String userMessageString = getIntent().getStringExtra(USER_MESSAGE); + if (userMessageString != null) { + userMessage.setText(userMessageString); + userMessage.setVisibility(VISIBLE); } } @@ -121,7 +125,11 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc } } - private void restoreState(Bundle savedInstance) { + protected void restoreState(Bundle savedInstance) { + super.restoreState(savedInstance); + if (savedInstance == null) { + return; + } if (savedInstance.getString(USER_MESSAGE) != null) { userMessage.setText(savedInstance.getString(USER_MESSAGE)); userMessage.setVisibility(VISIBLE); @@ -204,7 +212,7 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc void downloadVpnCertificate(Provider handledProvider) { provider = handledProvider; - ProviderAPICommand.execute(this, DOWNLOAD_CERTIFICATE, provider); + ProviderAPICommand.execute(this, DOWNLOAD_VPN_CERTIFICATE, provider); } protected Bundle bundleUsernameAndPassword(String username, String password) { @@ -381,7 +389,7 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc return; } - int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, -1); + int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED); Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); Provider handledProvider = resultData.getParcelable(PROVIDER_KEY); @@ -395,11 +403,10 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc handleReceivedErrors((Bundle) intent.getParcelableExtra(BROADCAST_RESULT_KEY)); break; - case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: + case ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE: successfullyFinished(handledProvider); - //activity.eip_fragment.updateEipService(); break; - case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: + case ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE: // TODO activity.setResult(RESULT_CANCELED); break; } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java index 41d2d849..e961b0a2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java @@ -53,10 +53,10 @@ import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; -import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; -import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_SET_UP; @@ -74,7 +74,7 @@ import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROVIDER_DETAILS; */ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity - implements NewProviderDialog.NewProviderDialogInterface, DownloadFailedDialog.DownloadFailedDialogInterface, ProviderAPIResultReceiver.Receiver { + implements NewProviderDialog.NewProviderDialogInterface, ProviderSetupFailedDialog.DownloadFailedDialogInterface, ProviderAPIResultReceiver.Receiver { @InjectView(R.id.provider_list) protected ListView providerListView; @@ -91,7 +91,8 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity final protected static String PROVIDER_NOT_SET = "PROVIDER NOT SET"; final protected static String SETTING_UP_PROVIDER = "PROVIDER GETS SET"; final private static String SHOWING_PROVIDER_DETAILS = "SHOWING PROVIDER DETAILS"; - final private static String PENDING_SHOW_FAILED_DIALOG = "SHOW FAILED DIALOG"; + final private static String PENDING_SHOW_FAILED_DIALOG = "SHOW FAILED DIALOG PENDING"; + final private static String SHOW_FAILED_DIALOG = "SHOW FAILED DIALOG"; final private static String REASON_TO_FAIL = "REASON TO FAIL"; final protected static String SERVICES_RETRIEVED = "SERVICES RETRIEVED"; @@ -117,19 +118,24 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity } @Override - protected void onSaveInstanceState(@NotNull Bundle outState) { + public void onSaveInstanceState(@NotNull Bundle outState) { outState.putString(ACTIVITY_STATE, mConfigState.getAction()); - outState.putParcelable(PROVIDER_KEY, provider); - - DialogFragment dialogFragment = (DialogFragment) fragmentManager.findFragmentByTag(DownloadFailedDialog.TAG); - if (dialogFragment != null) { - outState.putString(REASON_TO_FAIL, reasonToFail); - dialogFragment.dismiss(); - } + outState.putString(REASON_TO_FAIL, reasonToFail); super.onSaveInstanceState(outState); } + protected void restoreState(Bundle savedInstanceState) { + super.restoreState(savedInstanceState); + if (savedInstanceState == null) { + return; + } + mConfigState.setAction(savedInstanceState.getString(ACTIVITY_STATE, PROVIDER_NOT_SET)); + if (savedInstanceState.containsKey(REASON_TO_FAIL)) { + reasonToFail = savedInstanceState.getString(REASON_TO_FAIL); + } + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -137,28 +143,8 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity providerManager = ProviderManager.getInstance(getAssets(), getExternalFilesDir(null)); setUpInitialUI(); - initProviderList(); - - if (savedInstanceState != null) - restoreState(savedInstanceState); - } - - private void restoreState(Bundle savedInstanceState) { - - provider = savedInstanceState.getParcelable(Provider.KEY); - mConfigState.setAction(savedInstanceState.getString(ACTIVITY_STATE, PROVIDER_NOT_SET)); - - reasonToFail = savedInstanceState.getString(REASON_TO_FAIL); - if(reasonToFail != null) { - showDownloadFailedDialog(); - } - - if (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) || - PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction()) - ) { - showProgressBar(); - } + restoreState(savedInstanceState); } @Override @@ -166,15 +152,17 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity Log.d(TAG, "resuming with ConfigState: " + mConfigState.getAction()); super.onResume(); setUpProviderAPIResultReceiver(); - hideProgressBar(); isActivityShowing = true; if (SETTING_UP_PROVIDER.equals(mConfigState.getAction())) { showProgressBar(); checkProviderSetUp(); } else if (PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) { + showProgressBar(); showDownloadFailedDialog(); + } else if (SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) { + showProgressBar(); } else if (SHOWING_PROVIDER_DETAILS.equals(mConfigState.getAction())) { - cancelAndShowAllProviders(); + cancelSettingUpProvider(); } } @@ -257,7 +245,7 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity @OnItemClick(R.id.provider_list) void onItemSelected(int position) { if (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) || - PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) { + SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) { return; } @@ -276,10 +264,9 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity @Override public void onBackPressed() { if (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) || - PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) { + SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) { stopSettingUpProvider(); } else { - askDashboardToQuitApp(); super.onBackPressed(); } } @@ -291,11 +278,13 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity @Override public void cancelSettingUpProvider() { mConfigState.setAction(PROVIDER_NOT_SET); + provider = null; hideProgressBar(); } @Override public void updateProviderDetails() { + mConfigState.setAction(SETTING_UP_PROVIDER); ProviderAPICommand.execute(this, UPDATE_PROVIDER_DETAILS, provider); } @@ -303,17 +292,11 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity ProviderAPICommand.execute(this, PROVIDER_SET_UP, provider, providerAPIResultReceiver); } - private void askDashboardToQuitApp() { - Intent askQuit = new Intent(); - askQuit.putExtra(APP_ACTION_QUIT, APP_ACTION_QUIT); - setResult(RESULT_CANCELED, askQuit); - } - /** * Asks ProviderApiService to download an anonymous (anon) VPN certificate. */ private void downloadVpnCertificate() { - ProviderAPICommand.execute(this, DOWNLOAD_CERTIFICATE, provider); + ProviderAPICommand.execute(this, DOWNLOAD_VPN_CERTIFICATE, provider); } /** @@ -345,20 +328,23 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity */ public void showDownloadFailedDialog() { try { - FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(DownloadFailedDialog.TAG); + mConfigState.setAction(SHOW_FAILED_DIALOG); + FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(ProviderSetupFailedDialog.TAG); DialogFragment newFragment; try { JSONObject errorJson = new JSONObject(reasonToFail); - newFragment = DownloadFailedDialog.newInstance(provider, errorJson); + newFragment = ProviderSetupFailedDialog.newInstance(provider, errorJson); } catch (JSONException e) { e.printStackTrace(); - newFragment = DownloadFailedDialog.newInstance(provider, reasonToFail); + newFragment = ProviderSetupFailedDialog.newInstance(provider, reasonToFail); + } catch (NullPointerException e) { + //reasonToFail was null + return; } - newFragment.show(fragmentTransaction, DownloadFailedDialog.TAG); + newFragment.show(fragmentTransaction, ProviderSetupFailedDialog.TAG); } catch (IllegalStateException e) { e.printStackTrace(); mConfigState.setAction(PENDING_SHOW_FAILED_DIALOG); - mConfigState.putExtra(REASON_TO_FAIL, reasonToFail); } } @@ -388,11 +374,6 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity return true; } - public void cancelAndShowAllProviders() { - mConfigState.setAction(PROVIDER_NOT_SET); - provider = null; - } - public class ProviderAPIBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -405,7 +386,7 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity if (mConfigState.getAction() != null && mConfigState.getAction().equalsIgnoreCase(SETTING_UP_PROVIDER)) { - int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, -1); + int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED); Log.d(TAG, "Broadcast resultCode: " + Integer.toString(resultCode)); Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); @@ -419,10 +400,10 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity case PROVIDER_NOK: handleProviderSetupFailed(resultData); break; - case CORRECTLY_DOWNLOADED_CERTIFICATE: + case CORRECTLY_DOWNLOADED_VPN_CERTIFICATE: handleCorrectlyDownloadedCertificate(handledProvider); break; - case INCORRECTLY_DOWNLOADED_CERTIFICATE: + case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE: handleIncorrectlyDownloadedCertificate(); break; } diff --git a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java b/app/src/main/java/se/leap/bitmaskclient/ProviderSetupFailedDialog.java index 8a6d981d..5bd9575e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderSetupFailedDialog.java @@ -22,12 +22,13 @@ import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; import org.json.JSONObject; -import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.DEFAULT; -import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.valueOf; +import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.DEFAULT; +import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.valueOf; import static se.leap.bitmaskclient.ProviderAPI.ERRORID; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; @@ -36,9 +37,12 @@ import static se.leap.bitmaskclient.ProviderAPI.ERRORS; * * @author parmegv */ -public class DownloadFailedDialog extends DialogFragment { +public class ProviderSetupFailedDialog extends DialogFragment { public static String TAG = "downloaded_failed_dialog"; + private final static String KEY_PROVIDER = "key provider"; + private final static String KEY_REASON_TO_FAIL = "key reason to fail"; + private final static String KEY_DOWNLOAD_ERROR = "key download error"; private String reasonToFail; private DOWNLOAD_ERRORS downloadError = DEFAULT; @@ -55,7 +59,7 @@ public class DownloadFailedDialog extends DialogFragment { * @return a new instance of this DialogFragment. */ public static DialogFragment newInstance(Provider provider, String reasonToFail) { - DownloadFailedDialog dialogFragment = new DownloadFailedDialog(); + ProviderSetupFailedDialog dialogFragment = new ProviderSetupFailedDialog(); dialogFragment.reasonToFail = reasonToFail; dialogFragment.provider = provider; return dialogFragment; @@ -65,7 +69,7 @@ public class DownloadFailedDialog extends DialogFragment { * @return a new instance of this DialogFragment. */ public static DialogFragment newInstance(Provider provider, JSONObject errorJson) { - DownloadFailedDialog dialogFragment = new DownloadFailedDialog(); + ProviderSetupFailedDialog dialogFragment = new ProviderSetupFailedDialog(); dialogFragment.provider = provider; try { if (errorJson.has(ERRORS)) { @@ -86,6 +90,12 @@ public class DownloadFailedDialog extends DialogFragment { } @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + restoreFromSavedInstance(savedInstanceState); + } + + @Override @NonNull public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); @@ -93,15 +103,13 @@ public class DownloadFailedDialog extends DialogFragment { .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { interfaceWithConfigurationWizard.cancelSettingUpProvider(); - dialog.dismiss(); } }); -switch (downloadError) { + switch (downloadError) { case ERROR_CORRUPTED_PROVIDER_JSON: builder.setPositiveButton(R.string.update_provider_details, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - dismiss(); interfaceWithConfigurationWizard.updateProviderDetails(); } }); @@ -111,7 +119,6 @@ switch (downloadError) { builder.setPositiveButton(R.string.update_certificate, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - dismiss(); interfaceWithConfigurationWizard.updateProviderDetails(); } }); @@ -119,7 +126,6 @@ switch (downloadError) { default: builder.setPositiveButton(R.string.retry, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - dismiss(); interfaceWithConfigurationWizard.retrySetUpProvider(provider); } }); @@ -157,4 +163,26 @@ switch (downloadError) { dialog.dismiss(); } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putParcelable(KEY_PROVIDER, provider); + outState.putString(KEY_REASON_TO_FAIL, reasonToFail); + outState.putString(KEY_DOWNLOAD_ERROR, downloadError.toString()); + } + + private void restoreFromSavedInstance(Bundle savedInstanceState) { + if (savedInstanceState == null) { + return; + } + if (savedInstanceState.containsKey(KEY_PROVIDER)) { + this.provider = savedInstanceState.getParcelable(KEY_PROVIDER); + } + if (savedInstanceState.containsKey(KEY_REASON_TO_FAIL)) { + this.reasonToFail = savedInstanceState.getString(KEY_REASON_TO_FAIL); + } + if (savedInstanceState.containsKey(KEY_DOWNLOAD_ERROR)) { + this.downloadError = valueOf(savedInstanceState.getString(KEY_DOWNLOAD_ERROR)); + } + } } 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 9c7f6d1a..cbce1a81 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -16,7 +16,6 @@ */ package se.leap.bitmaskclient.eip; -import android.app.Activity; import android.app.IntentService; import android.content.Intent; import android.content.SharedPreferences; @@ -33,6 +32,9 @@ import java.lang.ref.WeakReference; import de.blinkt.openvpn.LaunchVPN; import se.leap.bitmaskclient.OnBootReceiver; +import static android.app.Activity.RESULT_CANCELED; +import static android.app.Activity.RESULT_OK; +import static android.content.Intent.CATEGORY_DEFAULT; import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; @@ -41,7 +43,6 @@ import static se.leap.bitmaskclient.Constants.EIP_ACTION_IS_RUNNING; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN; import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_UPDATE; import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES; import static se.leap.bitmaskclient.Constants.EIP_RECEIVER; import static se.leap.bitmaskclient.Constants.EIP_REQUEST; @@ -49,6 +50,8 @@ import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; +import static se.leap.bitmaskclient.MainActivityErrorDialog.DOWNLOAD_ERRORS.ERROR_INVALID_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid; /** * EIP is the abstract base class for interacting with and managing the Encrypted @@ -62,16 +65,14 @@ import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; */ public final class EIP extends IntentService { - public final static String TAG = EIP.class.getSimpleName(); - public final static String SERVICE_API_PATH = "config/eip-service.json"; + public final static String TAG = EIP.class.getSimpleName(), + SERVICE_API_PATH = "config/eip-service.json", + ERRORS = "errors", + ERROR_ID = "errorID"; private WeakReference<ResultReceiver> mReceiverRef = new WeakReference<>(null); private SharedPreferences preferences; - private JSONObject eipDefinition; - private GatewaysManager gatewaysManager = new GatewaysManager(); - private Gateway gateway; - public EIP() { super(TAG); } @@ -80,9 +81,6 @@ public final class EIP extends IntentService { public void onCreate() { super.onCreate(); preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); - eipDefinition = eipDefinitionFromPreferences(); - if (gatewaysManager.isEmpty()) - gatewaysFromPreferences(); } @Override @@ -110,11 +108,8 @@ public final class EIP extends IntentService { case EIP_ACTION_IS_RUNNING: isRunning(); break; - case EIP_ACTION_UPDATE: - updateEIPService(); - break; case EIP_ACTION_CHECK_CERT_VALIDITY: - checkCertValidity(); + checkVPNCertificateValidity(); break; } } @@ -125,21 +120,29 @@ public final class EIP extends IntentService { * It also sets up early routes. */ private void startEIP(boolean earlyRoutes) { + if (!EipStatus.getInstance().isBlockingVpnEstablished() && earlyRoutes) { + earlyRoutes(); + } + + Bundle result = new Bundle(); + if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)){ preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit(); } - if (gatewaysManager.isEmpty()) - updateEIPService(); - if (!EipStatus.getInstance().isBlockingVpnEstablished() && earlyRoutes) { - earlyRoutes(); + + GatewaysManager gatewaysManager = gatewaysFromPreferences(); + if (!isVPNCertificateValid()){ + setErrorResult(result, vpn_certificate_is_invalid, ERROR_INVALID_VPN_CERTIFICATE.toString()); + tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED, result); + return; } - gateway = gatewaysManager.select(); + Gateway gateway = gatewaysManager.select(); if (gateway != null && gateway.getProfile() != null) { - launchActiveGateway(); - tellToReceiverOrBroadcast(EIP_ACTION_START, Activity.RESULT_OK); + launchActiveGateway(gateway); + tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_OK); } else - tellToReceiverOrBroadcast(EIP_ACTION_START, Activity.RESULT_CANCELED); + tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED); } /** @@ -149,14 +152,12 @@ public final class EIP extends IntentService { private void startEIPAlwaysOnVpn() { Log.d(TAG, "startEIPAlwaysOnVpn vpn"); - if (gatewaysManager.isEmpty()) - updateEIPService(); - - gateway = gatewaysManager.select(); + GatewaysManager gatewaysManager = gatewaysFromPreferences(); + Gateway gateway = gatewaysManager.select(); if (gateway != null && gateway.getProfile() != null) { Log.d(TAG, "startEIPAlwaysOnVpn eip launch avtive gateway vpn"); - launchActiveGateway(); + launchActiveGateway(gateway); } else { Log.d(TAG, "startEIPAlwaysOnVpn no active profile available!"); } @@ -172,7 +173,7 @@ public final class EIP extends IntentService { startActivity(voidVpnLauncher); } - private void launchActiveGateway() { + private void launchActiveGateway(Gateway gateway) { Intent intent = new Intent(this, LaunchVPN.class); intent.setAction(Intent.ACTION_MAIN); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -182,10 +183,11 @@ public final class EIP extends IntentService { } private void stopEIP() { + // TODO stop eip from here if possible... EipStatus eipStatus = EipStatus.getInstance(); - int resultCode = Activity.RESULT_CANCELED; + int resultCode = RESULT_CANCELED; if (eipStatus.isConnected() || eipStatus.isConnecting()) - resultCode = Activity.RESULT_OK; + resultCode = RESULT_OK; tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode); } @@ -198,22 +200,11 @@ public final class EIP extends IntentService { private void isRunning() { EipStatus eipStatus = EipStatus.getInstance(); int resultCode = (eipStatus.isConnected()) ? - Activity.RESULT_OK : - Activity.RESULT_CANCELED; + RESULT_OK : + RESULT_CANCELED; tellToReceiverOrBroadcast(EIP_ACTION_IS_RUNNING, resultCode); } - /** - * Loads eip-service.json from SharedPreferences, delete previous vpn profiles and add new gateways. - * TODO Implement API call to refresh eip-service.json from the provider - */ - private void updateEIPService() { - eipDefinition = eipDefinitionFromPreferences(); - if (eipDefinition.length() > 0) - updateGateways(); - tellToReceiverOrBroadcast(EIP_ACTION_UPDATE, Activity.RESULT_OK); - } - private JSONObject eipDefinitionFromPreferences() { JSONObject result = new JSONObject(); try { @@ -228,34 +219,25 @@ public final class EIP extends IntentService { return result; } - private void updateGateways() { - gatewaysManager.clearGatewaysAndProfiles(); - gatewaysManager.fromEipServiceJson(eipDefinition); - gatewaysToPreferences(); + private GatewaysManager gatewaysFromPreferences() { + GatewaysManager gatewaysManager = new GatewaysManager(this, preferences); + gatewaysManager.fromEipServiceJson(eipDefinitionFromPreferences()); + return gatewaysManager; } - private void gatewaysFromPreferences() { - String gatewaysString = preferences.getString(Gateway.TAG, ""); - gatewaysManager = new GatewaysManager(this, preferences); - gatewaysManager.addFromString(gatewaysString); - preferences.edit().remove(Gateway.TAG).apply(); - } - - private void gatewaysToPreferences() { - String gateways_string = gatewaysManager.toString(); - preferences.edit().putString(Gateway.TAG, gateways_string).commit(); + private void checkVPNCertificateValidity() { + int resultCode = isVPNCertificateValid() ? + RESULT_OK : + RESULT_CANCELED; + tellToReceiverOrBroadcast(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); } - private void checkCertValidity() { + private boolean isVPNCertificateValid() { VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); - int resultCode = validator.isValid() ? - Activity.RESULT_OK : - Activity.RESULT_CANCELED; - tellToReceiverOrBroadcast(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); + return validator.isValid(); } - private void tellToReceiverOrBroadcast(String action, int resultCode) { - Bundle resultData = new Bundle(); + private void tellToReceiverOrBroadcast(String action, int resultCode, Bundle resultData) { resultData.putString(EIP_REQUEST, action); if (mReceiverRef.get() != null) { mReceiverRef.get().send(resultCode, resultData); @@ -264,13 +246,33 @@ public final class EIP extends IntentService { } } + private void tellToReceiverOrBroadcast(String action, int resultCode) { + tellToReceiverOrBroadcast(action, resultCode, new Bundle()); + } + private void broadcastEvent(int resultCode , Bundle resultData) { Intent intentUpdate = new Intent(BROADCAST_EIP_EVENT); - intentUpdate.addCategory(Intent.CATEGORY_DEFAULT); + intentUpdate.addCategory(CATEGORY_DEFAULT); intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode); intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData); Log.d(TAG, "sending broadcast"); LocalBroadcastManager.getInstance(this).sendBroadcast(intentUpdate); } + Bundle setErrorResult(Bundle result, int errorMessageId, String errorId) { + JSONObject errorJson = new JSONObject(); + addErrorMessageToJson(errorJson, getResources().getString(errorMessageId), errorId); + result.putString(ERRORS, errorJson.toString()); + result.putBoolean(BROADCAST_RESULT_KEY, false); + return result; + } + + private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage, String errorId) { + try { + jsonObject.put(ERRORS, errorMessage); + jsonObject.put(ERROR_ID, errorId); + } catch (JSONException e) { + e.printStackTrace(); + } + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java index 1c2ae5da..d2c8b4fc 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java @@ -12,7 +12,6 @@ import org.jetbrains.annotations.Nullable; import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_UPDATE; import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES; import static se.leap.bitmaskclient.Constants.EIP_RECEIVER; @@ -45,14 +44,6 @@ public class EipCommand { context.startService(vpnIntent); } - public static void updateEipService(@NonNull Context context, ResultReceiver resultReceiver) { - execute(context, EIP_ACTION_UPDATE, resultReceiver, null); - } - - public static void updateEipService(@NonNull Context context) { - execute(context, EIP_ACTION_UPDATE, null, null); - } - public static void startVPN(@NonNull Context context, boolean earlyRoutes) { Intent baseIntent = new Intent(); baseIntent.putExtra(EIP_EARLY_ROUTES, earlyRoutes); 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 197a080b..03dd9d05 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java @@ -48,12 +48,16 @@ public class VpnCertificateValidator { return false; } - X509Certificate certificate_x509 = ConfigHelper.parseX509CertificateFromString(certificate); - return isValid(certificate_x509); + X509Certificate x509Certificate = ConfigHelper.parseX509CertificateFromString(certificate); + return isValid(x509Certificate); } private boolean isValid(X509Certificate certificate) { + if (certificate == null) { + return false; + } + Calendar offsetDate = calculateOffsetCertificateValidity(certificate); try { certificate.checkValidity(offsetDate.getTime()); |