summaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/de/blinkt/openvpn/LaunchVPN.java13
-rw-r--r--app/src/main/java/de/blinkt/openvpn/VpnProfile.java92
-rw-r--r--app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java4
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java14
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java45
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java19
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVpnService.java84
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Dashboard.java63
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EIP.java108
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java306
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipStatusReceiver.java17
12 files changed, 444 insertions, 323 deletions
diff --git a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
index a39e780a..3f80eef0 100644
--- a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
@@ -4,8 +4,6 @@ import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.R;
-import java.io.IOException;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
@@ -20,13 +18,17 @@ import android.text.InputType;
import android.text.TextUtils;
import android.text.method.PasswordTransformationMethod;
import android.view.View;
-import android.widget.*;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+import java.io.IOException;
import de.blinkt.openvpn.activities.LogWindow;
-import de.blinkt.openvpn.core.VpnStatus;
-import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
import de.blinkt.openvpn.core.ProfileManager;
import de.blinkt.openvpn.core.VPNLaunchHelper;
+import de.blinkt.openvpn.core.VpnStatus;
+import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
/**
* This Activity actually handles two stages of a launcher shortcut's life cycle.
@@ -111,7 +113,6 @@ public class LaunchVPN extends Activity {
}
}
-
@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
index 0166eb98..d44d0f5a 100644
--- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
+++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
@@ -4,6 +4,7 @@ import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.R;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -14,6 +15,7 @@ import android.os.Build;
import android.preference.PreferenceManager;
import android.security.KeyChain;
import android.security.KeyChainException;
+import android.text.TextUtils;
import android.util.Base64;
import org.spongycastle.util.io.pem.PemObject;
@@ -115,8 +117,8 @@ public class VpnProfile implements Serializable {
public boolean mUseDefaultRoute = true;
public boolean mUsePull = true;
public String mCustomRoutes;
- public boolean mCheckRemoteCN = false;
- public boolean mExpectTLSCert = true;
+ public boolean mCheckRemoteCN = true;
+ public boolean mExpectTLSCert = false;
public String mRemoteCN = "";
public String mPassword = "";
public String mUsername = "";
@@ -181,6 +183,7 @@ public class VpnProfile implements Serializable {
mUseDefaultRoute = false;
mUseDefaultRoutev6 = false;
mExpectTLSCert = false;
+ mCheckRemoteCN = false;
mPersistTun = false;
mAllowLocalLAN = true;
}
@@ -199,10 +202,7 @@ public class VpnProfile implements Serializable {
public void upgradeProfile(){
if(mProfileVersion< 2) {
/* default to the behaviour the OS used */
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
- mAllowLocalLAN = true;
- else
- mAllowLocalLAN = false;
+ mAllowLocalLAN = Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT;
}
mProfileVersion= CURRENT_PROFILE_VERSION;
@@ -213,7 +213,7 @@ public class VpnProfile implements Serializable {
File cacheDir = context.getCacheDir();
String cfg = "";
- // Enable managment interface
+ // Enable management interface
cfg += "# Enables connection to GUI\n";
cfg += "management ";
@@ -230,6 +230,9 @@ public class VpnProfile implements Serializable {
cfg += "machine-readable-output\n";
+ // Users are confused by warnings that are misleading...
+ cfg += "ifconfig-nowarn\n";
+
boolean useTLSClient = (mAuthenticationType != TYPE_STATICKEYS);
@@ -327,7 +330,7 @@ public class VpnProfile implements Serializable {
else
cfg += insertFileData("tls-auth", mTLSAuthFilename);
- if (nonNull(mTLSAuthDirection)) {
+ if (!TextUtils.isEmpty(mTLSAuthDirection)) {
cfg += "key-direction ";
cfg += mTLSAuthDirection;
cfg += "\n";
@@ -336,10 +339,10 @@ public class VpnProfile implements Serializable {
}
if (!mUsePull) {
- if (nonNull(mIPv4Address))
+ if (!TextUtils.isEmpty(mIPv4Address))
cfg += "ifconfig " + cidrToIPAndNetmask(mIPv4Address) + "\n";
- if (nonNull(mIPv6Address))
+ if (!TextUtils.isEmpty(mIPv6Address))
cfg += "ifconfig-ipv6 " + mIPv6Address + "\n";
}
@@ -377,11 +380,11 @@ public class VpnProfile implements Serializable {
cfg += routes;
if (mOverrideDNS || !mUsePull) {
- if (nonNull(mDNS1))
+ if (!TextUtils.isEmpty(mDNS1))
cfg += "dhcp-option DNS " + mDNS1 + "\n";
- if (nonNull(mDNS2))
+ if (!TextUtils.isEmpty(mDNS2))
cfg += "dhcp-option DNS " + mDNS2 + "\n";
- if (nonNull(mSearchDomain))
+ if (!TextUtils.isEmpty(mSearchDomain))
cfg += "dhcp-option DOMAIN " + mSearchDomain + "\n";
}
@@ -422,11 +425,11 @@ public class VpnProfile implements Serializable {
cfg += "remote-cert-tls server\n";
}
- if (nonNull(mCipher)) {
+ if (!TextUtils.isEmpty(mCipher)) {
cfg += "cipher " + mCipher + "\n";
}
- if (nonNull(mAuth)) {
+ if (!TextUtils.isEmpty(mAuth)) {
cfg += "auth " + mAuth + "\n";
}
@@ -488,13 +491,6 @@ public class VpnProfile implements Serializable {
}
}
- private boolean nonNull(String val) {
- if (val == null || val.equals(""))
- return false;
- else
- return true;
- }
-
private Collection<String> getCustomRoutes(String routes) {
Vector<String> cidrRoutes = new Vector<String>();
if (routes == null) {
@@ -636,8 +632,8 @@ public class VpnProfile implements Serializable {
synchronized String[] getKeyStoreCertificates(Context context,int tries) {
PrivateKey privateKey = null;
- X509Certificate[] cachain;
- Exception exp=null;
+ X509Certificate[] caChain;
+ Exception exp;
try {
privateKey = KeyChain.getPrivateKey(context, mAlias);
mPrivateKey = privateKey;
@@ -645,18 +641,18 @@ public class VpnProfile implements Serializable {
String keystoreChain = null;
- cachain = KeyChain.getCertificateChain(context, mAlias);
- if(cachain == null)
+ caChain = KeyChain.getCertificateChain(context, mAlias);
+ if(caChain == null)
throw new NoCertReturnedException("No certificate returned from Keystore");
- if (cachain.length <= 1 && !nonNull(mCaFilename)) {
+ if (caChain.length <= 1 && TextUtils.isEmpty(mCaFilename)) {
VpnStatus.logMessage(VpnStatus.LogLevel.ERROR, "", context.getString(R.string.keychain_nocacert));
} else {
StringWriter ksStringWriter = new StringWriter();
PemWriter pw = new PemWriter(ksStringWriter);
- for (int i = 1; i < cachain.length; i++) {
- X509Certificate cert = cachain[i];
+ for (int i = 1; i < caChain.length; i++) {
+ X509Certificate cert = caChain[i];
pw.writeObject(new PemObject("CERTIFICATE", cert.getEncoded()));
}
pw.close();
@@ -665,7 +661,7 @@ public class VpnProfile implements Serializable {
String caout = null;
- if (nonNull(mCaFilename)) {
+ if (!TextUtils.isEmpty(mCaFilename)) {
try {
Certificate cacert = X509Utils.getCertificateFromFile(mCaFilename);
StringWriter caoutWriter = new StringWriter();
@@ -684,8 +680,8 @@ public class VpnProfile implements Serializable {
StringWriter certout = new StringWriter();
- if (cachain.length >= 1) {
- X509Certificate usercert = cachain[0];
+ if (caChain.length >= 1) {
+ X509Certificate usercert = caChain[0];
PemWriter upw = new PemWriter(certout);
upw.writeObject(new PemObject("CERTIFICATE", usercert.getEncoded()));
@@ -730,15 +726,14 @@ public class VpnProfile implements Serializable {
}
return getKeyStoreCertificates(context, tries-1);
}
- if (exp != null) {
- exp.printStackTrace();
- VpnStatus.logError(R.string.keyChainAccessError, exp.getLocalizedMessage());
- VpnStatus.logError(R.string.keychain_access);
- if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
- if (!mAlias.matches("^[a-zA-Z0-9]$")) {
- VpnStatus.logError(R.string.jelly_keystore_alphanumeric_bug);
- }
+ exp.printStackTrace();
+ VpnStatus.logError(R.string.keyChainAccessError, exp.getLocalizedMessage());
+
+ VpnStatus.logError(R.string.keychain_access);
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
+ if (!mAlias.matches("^[a-zA-Z0-9]$")) {
+ VpnStatus.logError(R.string.jelly_keystore_alphanumeric_bug);
}
}
return null;
@@ -801,7 +796,7 @@ public class VpnProfile implements Serializable {
}
public boolean requireTLSKeyPassword() {
- if (!nonNull(mClientKeyFilename))
+ if (TextUtils.isEmpty(mClientKeyFilename))
return false;
String data = "";
@@ -842,13 +837,13 @@ public class VpnProfile implements Serializable {
}
if (mAuthenticationType == TYPE_CERTIFICATES || mAuthenticationType == TYPE_USERPASS_CERTIFICATES) {
- if (requireTLSKeyPassword() && !nonNull(mKeyPassword))
+ if (requireTLSKeyPassword() && TextUtils.isEmpty(mKeyPassword))
if (mTransientPCKS12PW == null) {
return R.string.private_key_password;
}
}
- if (isUserPWAuth() && !(nonNull(mUsername) && (nonNull(mPassword) || mTransientPW != null))) {
+ if (isUserPWAuth() && !(!TextUtils.isEmpty(mUsername) && (!TextUtils.isEmpty(mPassword) || mTransientPW != null))) {
return R.string.password;
}
return 0;
@@ -893,12 +888,15 @@ public class VpnProfile implements Serializable {
try {
+ /* ECB is perfectly fine in this special case, since we are using it for
+ the public/private part in the TLS exchange
+ */
+ @SuppressLint("GetInstance")
+ Cipher rsaSigner = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
- Cipher rsasinger = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
-
- rsasinger.init(Cipher.ENCRYPT_MODE, privkey);
+ rsaSigner.init(Cipher.ENCRYPT_MODE, privkey);
- byte[] signed_bytes = rsasinger.doFinal(data);
+ byte[] signed_bytes = rsaSigner.doFinal(data);
return Base64.encodeToString(signed_bytes, Base64.NO_WRAP);
} catch (NoSuchAlgorithmException e) {
diff --git a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java b/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
index da011c98..8e418053 100644
--- a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
@@ -43,8 +43,8 @@ public class DisconnectVPN extends Activity implements DialogInterface.OnClickLi
}
@Override
- protected void onStop() {
- super.onStop();
+ protected void onPause() {
+ super.onPause();
unbindService(mConnection);
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
index 378b6b92..d23b521f 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
@@ -1,7 +1,5 @@
package de.blinkt.openvpn.core;
-import de.blinkt.openvpn.VpnProfile;
-
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
@@ -10,6 +8,8 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Vector;
+import de.blinkt.openvpn.VpnProfile;
+
//! Openvpn Config FIle Parser, probably not 100% accurate but close enough
// And remember, this is valid :)
@@ -31,11 +31,17 @@ public class ConfigParser {
BufferedReader br =new BufferedReader(reader);
+ int lineno=0;
while (true){
String line = br.readLine();
+ lineno++;
if(line==null)
break;
+ if (lineno==1 && (line.startsWith("PK\003\004")
+ || (line.startsWith("PK\007\008"))))
+ throw new ConfigParseError("Input looks like a ZIP Archive. Import is only possible for OpenVPN config files (.ovpn/.conf)");
+
// Check for OpenVPN Access Server Meta information
if (line.startsWith("# OVPN_ACCESS_SERVER_")) {
Vector<String> metaarg = parsemeta(line);
@@ -440,8 +446,8 @@ public class ConfigParser {
}
Vector<String> rport = getOption("rport", 1,1);
- if(port!=null){
- np.mServerPort = port.get(1);
+ if(rport!=null){
+ np.mServerPort = rport.get(1);
}
Vector<String> proto = getOption("proto", 1,1);
diff --git a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
index 990e70d8..81a17ef9 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
@@ -5,6 +5,8 @@ import android.text.TextUtils;
import junit.framework.Assert;
+import org.jetbrains.annotations.NotNull;
+
import java.math.BigInteger;
import java.net.Inet6Address;
import java.util.*;
@@ -23,13 +25,18 @@ public class NetworkSpace {
private BigInteger lastAddress;
+ /**
+ * sorts the networks with following criteria:
+ * 1. compares first 1 of the network
+ * 2. smaller networks are returned as smaller
+ */
@Override
- public int compareTo(ipAddress another) {
+ public int compareTo(@NotNull ipAddress another) {
int comp = getFirstAddress().compareTo(another.getFirstAddress());
if (comp != 0)
return comp;
- // bigger mask means smaller address block
+
if (networkMask > another.networkMask)
return -1;
else if (another.networkMask == networkMask)
@@ -38,6 +45,22 @@ public class NetworkSpace {
return 1;
}
+ /**
+ * Warning ignores the included integer
+ *
+ * @param o
+ * the object to compare this instance with.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ipAddress))
+ return super.equals(o);
+
+
+ ipAddress on = (ipAddress) o;
+ return (networkMask == on.networkMask) && on.getFirstAddress().equals(getFirstAddress());
+ }
+
public ipAddress(CIDRIP ip, boolean include) {
included = include;
netAddress = BigInteger.valueOf(ip.getInt());
@@ -110,10 +133,10 @@ public class NetworkSpace {
public ipAddress[] split() {
- ipAddress firsthalf = new ipAddress(getFirstAddress(), networkMask + 1, included, isV4);
- ipAddress secondhalf = new ipAddress(firsthalf.getLastAddress().add(BigInteger.ONE), networkMask + 1, included, isV4);
- if (BuildConfig.DEBUG) Assert.assertTrue(secondhalf.getLastAddress().equals(getLastAddress()));
- return new ipAddress[]{firsthalf, secondhalf};
+ ipAddress firstHalf = new ipAddress(getFirstAddress(), networkMask + 1, included, isV4);
+ ipAddress secondHalf = new ipAddress(firstHalf.getLastAddress().add(BigInteger.ONE), networkMask + 1, included, isV4);
+ if (BuildConfig.DEBUG) Assert.assertTrue(secondHalf.getLastAddress().equals(getLastAddress()));
+ return new ipAddress[]{firstHalf, secondHalf};
}
String getIPv4Address() {
@@ -185,7 +208,7 @@ public class NetworkSpace {
return ipsDone;
while (currentNet!=null) {
- // Check if it and the next of it are compatbile
+ // Check if it and the next of it are compatible
ipAddress nextNet = networks.poll();
if (BuildConfig.DEBUG) Assert.assertNotNull(currentNet);
@@ -202,9 +225,12 @@ public class NetworkSpace {
// Simply forget our current network
currentNet=nextNet;
} else {
- // our currentnet is included in next and types differ. Need to split the next network
+ // our currentNet is included in next and types differ. Need to split the next network
ipAddress[] newNets = nextNet.split();
+
+ // TODO: The contains method of the Priority is stupid linear search
+
// First add the second half to keep the order in networks
if (!networks.contains(newNets[1]))
networks.add(newNets[1]);
@@ -226,6 +252,7 @@ public class NetworkSpace {
}
// This network is bigger than the next and last ip of current >= next
+ //noinspection StatementWithEmptyBody
if (currentNet.included == nextNet.included) {
// Next network is in included in our network with the same type,
// simply ignore the next and move on
@@ -238,7 +265,7 @@ public class NetworkSpace {
if (BuildConfig.DEBUG) {
Assert.assertTrue (newNets[1].getFirstAddress().equals(nextNet.getFirstAddress()));
Assert.assertTrue (newNets[1].getLastAddress().equals(currentNet.getLastAddress()));
- // Splitted second equal the next network, do not add it
+ // split second equal the next network, do not add it
}
networks.add(nextNet);
} else {
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
index 0de54ed7..67c05e7d 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
@@ -48,8 +48,6 @@ public class OpenVPNThread implements Runnable {
public void stopProcess() {
mProcess.destroy();
}
-
-
@Override
public void run() {
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
index 4cba4f5f..e6e5be25 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
@@ -9,11 +9,9 @@ import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
import android.util.Log;
-import org.jetbrains.annotations.NotNull;
+import junit.framework.Assert;
-import se.leap.bitmaskclient.R;
-import de.blinkt.openvpn.VpnProfile;
-import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
+import org.jetbrains.annotations.NotNull;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -22,7 +20,16 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Locale;
+import java.util.Vector;
+
+import se.leap.bitmaskclient.BuildConfig;
+import se.leap.bitmaskclient.R;
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
@@ -391,7 +398,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
*/
if(routeparts.length==5) {
- assert(routeparts[3].equals("dev"));
+ if (BuildConfig.DEBUG) Assert.assertEquals("dev", routeparts[3]);
mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], routeparts[4]);
} else if (routeparts.length >= 3) {
mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], null);
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnService.java
index 43b27212..0cf93de3 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnService.java
@@ -40,6 +40,8 @@ import de.blinkt.openvpn.core.VpnStatus.StateListener;
import static de.blinkt.openvpn.core.NetworkSpace.ipAddress;
import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_CONNECTED;
+import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED;
+import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_NONETWORK;
import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET;
import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT;
@@ -72,6 +74,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
private OpenVPNManagement mManagement;
private String mLastTunCfg;
private String mRemoteGW;
+ private Object mProcessLock = new Object();
// From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
public static String humanReadableByteCount(long bytes, boolean mbit) {
@@ -110,7 +113,9 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
}
private void endVpnService() {
- mProcessThread = null;
+ synchronized (mProcessLock) {
+ mProcessThread = null;
+ }
VpnStatus.removeByteCountListener(this);
unregisterDeviceStateReceiver();
ProfileManager.setConntectedVpnProfileDisconnected(this);
@@ -161,7 +166,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
mNotificationManager.notify(OPENVPN_STATUS, notification);
- startForeground(OPENVPN_STATUS, notification);
+ // startForeground(OPENVPN_STATUS, notification);
}
private int getIconByConnectionStatus(ConnectionStatus level) {
@@ -314,11 +319,12 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
mProfile = ProfileManager.get(this, profileUUID);
+
String startTitle = getString(R.string.start_vpn_title, mProfile.mName);
String startTicker = getString(R.string.start_vpn_ticker, mProfile.mName);
showNotification(startTitle, startTicker,
false, 0, LEVEL_CONNECTING_NO_SERVER_REPLY_YET);
-
+
// Set a flag that we are starting a new VPN
mStarting = true;
// Stop the previous session by interrupting the thread.
@@ -330,13 +336,14 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
//ignore
}
-
- if (mProcessThread != null) {
- mProcessThread.interrupt();
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- //ignore
+ synchronized (mProcessLock) {
+ if (mProcessThread != null) {
+ mProcessThread.interrupt();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ //ignore
+ }
}
}
// An old running VPN should now be exited
@@ -380,9 +387,10 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
processThread = new OpenVPNThread(this, argv, env, nativelibdir);
}
- mProcessThread = new Thread(processThread, "OpenVPNProcessThread");
- mProcessThread.start();
-
+ synchronized (mProcessLock) {
+ mProcessThread = new Thread(processThread, "OpenVPNProcessThread");
+ mProcessThread.start();
+ }
if (mDeviceStateReceiver != null)
unregisterDeviceStateReceiver();
@@ -416,11 +424,12 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
@Override
public void onDestroy() {
- if (mProcessThread != null) {
- mManagement.stopVPN();
-
- mProcessThread.interrupt();
+ synchronized (mProcessLock) {
+ if (mProcessThread != null) {
+ mManagement.stopVPN();
+ }
}
+
if (mDeviceStateReceiver != null) {
this.unregisterReceiver(mDeviceStateReceiver);
}
@@ -639,10 +648,10 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
mMtu = mtu;
mRemoteGW=null;
+ long netMaskAsInt = CIDRIP.getInt(netmask);
if (mLocalIP.len == 32 && !netmask.equals("255.255.255.255")) {
// get the netmask as IP
- long netMaskAsInt = CIDRIP.getInt(netmask);
int masklen;
if ("net30".equals(mode))
@@ -655,11 +664,18 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
if ((netMaskAsInt & mask) == (mLocalIP.getInt() & mask )) {
mLocalIP.len = masklen;
} else {
+ mLocalIP.len = 32;
if (!"p2p".equals(mode))
VpnStatus.logWarning(R.string.ip_not_cidr, local, netmask, mode);
- mRemoteGW=netmask;
}
}
+ if (("p2p".equals(mode)) && mLocalIP.len < 32 || "net30".equals("net30") && mLocalIP.len < 30) {
+ VpnStatus.logWarning(R.string.ip_looks_like_subnet, local, netmask, mode);
+ }
+
+
+ // Configurations are sometimes really broken...
+ mRemoteGW=netmask;
}
public void setLocalIPv6(String ipv6addr) {
@@ -686,17 +702,22 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
mDisplayBytecount = true;
mConnecttime = System.currentTimeMillis();
lowpriority = true;
- } else {
+ NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ mNotificationManager.cancel(OPENVPN_STATUS);
+ } else if(!mProfile.mPersistTun || mConnecttime == 0){
mDisplayBytecount = false;
- }
-
- // Other notifications are shown,
- // This also mean we are no longer connected, ignore bytecount messages until next
- // CONNECTED
- // Does not work :(
- String msg = getString(resid);
- String ticker = msg;
- showNotification(msg + " " + logmessage, ticker, lowpriority , 0, level);
+ String msg = getString(resid);
+ String ticker = msg;
+ showNotification(msg + " " + logmessage, ticker, lowpriority , 0, level);
+ } else if(mProfile.mPersistTun && level == LEVEL_NONETWORK) {
+ NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ mNotificationManager.cancel(OPENVPN_STATUS);
+ } else if(mProfile.mPersistTun && mConnecttime > 0) {
+ mDisplayBytecount = false;
+ String msg = "Traffic is blocked until the VPN becomes active.";
+ String ticker = msg;
+ showNotification(msg, ticker, lowpriority , 0, level);
+ }
}
}
@@ -717,9 +738,6 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
humanReadableByteCount(diffIn / OpenVPNManagement.mBytecountInterval, true),
humanReadableByteCount(out, false),
humanReadableByteCount(diffOut / OpenVPNManagement.mBytecountInterval, true));
-
- boolean lowpriority = !mNotificationAlwaysVisible;
- showNotification(netstat, null, lowpriority, mConnecttime, LEVEL_CONNECTED);
}
}
@@ -746,7 +764,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
} else {
String release = Build.VERSION.RELEASE;
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT && !release.startsWith("4.4.3")
- && !release.startsWith("4.4.4") && !release.startsWith("4.4.5"))
+ && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6"))
// There will be probably no 4.4.4 or 4.4.5 version, so don't waste effort to do parsing here
return "OPEN_AFTER_CLOSE";
else
diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index 117e45d8..fe546a21 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -33,6 +33,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
@@ -63,12 +64,14 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
public static final String PARAMETERS = "dashboard parameters";
public static final String START_ON_BOOT = "dashboard start on boot";
final public static String ON_BOOT = "dashboard on boot";
+ public static final String APP_VERSION = "bitmask version";
-
+
+ private EipServiceFragment eipFragment;
private ProgressBar mProgressBar;
private TextView eipStatus;
private static Context app;
- private static SharedPreferences preferences;
+ protected static SharedPreferences preferences;
private static Provider provider;
private TextView providerNameTV;
@@ -81,17 +84,15 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- app = this;
+ app = this;
PRNGFixes.apply();
- // mProgressBar = (ProgressBar) findViewById(R.id.progressbar_dashboard);
- // mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
- // eipStatus = (TextView) findViewById(R.id.eipStatus);
mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
-
+ handleVersion();
+
authed_eip = preferences.getBoolean(EIP.AUTHED_EIP, false);
if (preferences.getString(Provider.KEY, "").isEmpty())
startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
@@ -99,8 +100,21 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
buildDashboard(getIntent().getBooleanExtra(ON_BOOT, false));
}
+ private void handleVersion() {
+ try {
+ int versionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
+ int lastDetectedVersion = preferences.getInt(APP_VERSION, 0);
+ if(lastDetectedVersion == 0) // New install
+ getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).edit().putInt(APP_VERSION, versionCode);
+ else if(lastDetectedVersion < versionCode) {
+ }
+ } catch (NameNotFoundException e) {
+ }
+ }
+
@Override
protected void onDestroy() {
+
super.onDestroy();
}
@@ -122,7 +136,7 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
invalidateOptionsMenu();
if(data != null && data.hasExtra(LogInDialog.VERB)) {
View view = ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
- logInDialog(view, Bundle.EMPTY);
+ logInDialog(Bundle.EMPTY);
}
} else if(resultCode == RESULT_CANCELED && (data == null || data.hasExtra(ACTION_QUIT))) {
finish();
@@ -176,9 +190,9 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
FragmentManager fragMan = getFragmentManager();
if ( provider.hasEIP()){
- EipServiceFragment eipFragment = new EipServiceFragment();
- if (hide_and_turn_on_eip) {
- getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).edit().remove(Dashboard.START_ON_BOOT).commit();
+ eipFragment = new EipServiceFragment();
+ if (hide_and_turn_on_eip) {
+ getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).edit().remove(Dashboard.START_ON_BOOT).commit();
Bundle arguments = new Bundle();
arguments.putBoolean(EipServiceFragment.START_ON_BOOT, true);
eipFragment.setArguments(arguments);
@@ -197,8 +211,10 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
try {
provider_json = new JSONObject(getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE).getString(Provider.KEY, ""));
JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE);
-
- if(service_description.getBoolean(Provider.ALLOW_REGISTRATION)) {
+ boolean authed_eip = preferences.getBoolean(EIP.AUTHED_EIP, false);
+ boolean allow_registered_eip = service_description.getBoolean(Provider.ALLOW_REGISTRATION);
+ preferences.edit().putBoolean(EIP.ALLOWED_REGISTERED, allow_registered_eip);
+ if(allow_registered_eip) {
if(authed_eip) {
menu.findItem(R.id.login_button).setVisible(false);
menu.findItem(R.id.logout_button).setVisible(true);
@@ -238,15 +254,15 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
if (Provider.getInstance().hasEIP()){
if (getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).getBoolean(EIP.AUTHED_EIP, false)){
logOut();
- }
+ }
eipStop();
}
- getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).edit().remove(Provider.KEY).commit();
+ getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).edit().clear().commit();
startActivityForResult(new Intent(this,ConfigurationWizard.class), SWITCH_PROVIDER);
return true;
case R.id.login_button:
View view = ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
- logInDialog(view, Bundle.EMPTY);
+ logInDialog(Bundle.EMPTY);
return true;
case R.id.logout_button:
logOut();
@@ -305,6 +321,7 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
if(eipStatus == null) eipStatus = (TextView) findViewById(R.id.eipStatus);
if(eipStatus != null) eipStatus.setText("");
}
+ cancelAuthedEipOn();
}
/**
@@ -343,9 +360,9 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
/**
* Shows the log in dialog.
- * @param view from which the dialog is created.
*/
- public void logInDialog(View view, Bundle resultData) {
+ public void logInDialog(Bundle resultData) {
+ Log.d("Dashboard", "Log In Dialog");
FragmentTransaction fragment_transaction = getFragmentManager().beginTransaction();
Fragment previous_log_in_dialog = getFragmentManager().findFragmentByTag(LogInDialog.TAG);
if (previous_log_in_dialog != null) {
@@ -455,7 +472,7 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
//Cookie session_id = new BasicClientCookie(session_id_cookie_key, session_id_string);
downloadAuthedUserCertificate(/*session_id*/);
} else if(resultCode == ProviderAPI.SRP_AUTHENTICATION_FAILED) {
- logInDialog(getCurrentFocus(), resultData);
+ logInDialog(resultData);
} else if(resultCode == ProviderAPI.LOGOUT_SUCCESSFUL) {
authed_eip = false;
getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).edit().putBoolean(EIP.AUTHED_EIP, authed_eip).commit();
@@ -468,7 +485,7 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
} else if(resultCode == ProviderAPI.LOGOUT_FAILED) {
setResult(RESULT_CANCELED);
changeStatusMessage(resultCode);
- mProgressBar.setVisibility(ProgressBar.GONE);
+ mProgressBar.setVisibility(ProgressBar.GONE);
} else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) {
setResult(RESULT_OK);
changeStatusMessage(resultCode);
@@ -579,10 +596,4 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
mProgressBar.setVisibility(visibility);
}
-
- protected void setEipStatus(int status) {
- if(eipStatus == null)
- eipStatus = (TextView) findViewById(R.id.eipStatus);
- eipStatus.setText(status);
- }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/EIP.java b/app/src/main/java/se/leap/bitmaskclient/EIP.java
index 7374d5ed..41299318 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EIP.java
@@ -41,13 +41,16 @@ import de.blinkt.openvpn.core.OpenVpnManagementThread;
import de.blinkt.openvpn.core.OpenVpnService.LocalBinder;
import de.blinkt.openvpn.core.OpenVpnService;
import de.blinkt.openvpn.core.ProfileManager;
+import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
import java.io.IOException;
import java.io.StringReader;
+import java.lang.StringBuffer;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
@@ -87,6 +90,7 @@ public final class EIP extends IntentService {
public final static String STATUS = "eip status";
public final static String DATE_FROM_CERTIFICATE = "date from certificate";
public final static String ALLOWED_ANON = "allow_anonymous";
+ public final static String ALLOWED_REGISTERED = "allow_registration";
public final static String CERTIFICATE = "cert";
public final static String PRIVATE_KEY = "private_key";
public final static String KEY = "eip";
@@ -109,6 +113,10 @@ public final class EIP extends IntentService {
private static JSONObject eipDefinition = null;
private static OVPNGateway activeGateway = null;
+
+ protected static ConnectionStatus lastConnectionStatusLevel;
+ protected static boolean mIsDisconnecting = false;
+ protected static boolean mIsStarting = false;
public EIP(){
super("LEAPEIP");
@@ -169,10 +177,6 @@ public final class EIP extends IntentService {
Log.d(TAG, "isRunning() = " + is_connected);
}
-
- private boolean isConnected() {
- return getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).getString(STATUS, "").equalsIgnoreCase("LEVEL_CONNECTED");
- }
/**
* Initiates an EIP connection by selecting a gateway and preparing and sending an
@@ -207,6 +211,9 @@ public final class EIP extends IntentService {
Intent disconnect_vpn = new Intent(this, DisconnectVPN.class);
disconnect_vpn.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(disconnect_vpn);
+ mIsDisconnecting = true;
+ lastConnectionStatusLevel = ConnectionStatus.UNKNOWN_LEVEL; // Wait for the decision of the user
+ Log.d(TAG, "mIsDisconnecting = true");
}
if (mReceiver != null){
@@ -216,6 +223,10 @@ public final class EIP extends IntentService {
}
}
+ protected static boolean isConnected() {
+ return lastConnectionStatusLevel != null && lastConnectionStatusLevel.equals(ConnectionStatus.LEVEL_CONNECTED) && !mIsDisconnecting;
+ }
+
/**
* Loads eip-service.json from SharedPreferences and calls {@link updateGateways()}
* to parse gateway definitions.
@@ -335,31 +346,34 @@ public final class EIP extends IntentService {
private void checkCertValidity() {
String certificate_string = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).getString(CERTIFICATE, "");
- String date_from_certificate_string = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).getString(DATE_FROM_CERTIFICATE, Calendar.getInstance().getTime().toString());
- X509Certificate certificate_x509 = ConfigHelper.parseX509CertificateFromString(certificate_string);
+ if(!certificate_string.isEmpty()) {
+ String date_from_certificate_string = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).getString(DATE_FROM_CERTIFICATE, certificate_date_format.format(Calendar.getInstance().getTime()).toString());
+ X509Certificate certificate_x509 = ConfigHelper.parseX509CertificateFromString(certificate_string);
- Calendar offset_date = Calendar.getInstance();
- try {
- long difference = Math.abs(certificate_date_format.parse(date_from_certificate_string).getTime() - certificate_x509.getNotAfter().getTime())/2;
- long current_date_millis = offset_date.getTimeInMillis();
- offset_date.setTimeInMillis(current_date_millis + difference);
- Log.d(TAG, "certificate not after = " + certificate_x509.getNotAfter());
- } catch(ParseException e) {
- e.printStackTrace();
- }
+ Calendar offset_date = Calendar.getInstance();
+ try {
+ Date date_from_certificate = certificate_date_format.parse(date_from_certificate_string);
+ long difference = Math.abs(date_from_certificate.getTime() - certificate_x509.getNotAfter().getTime())/2;
+ long current_date_millis = offset_date.getTimeInMillis();
+ offset_date.setTimeInMillis(current_date_millis + difference);
+ Log.d(TAG, "certificate not after = " + certificate_x509.getNotAfter());
+ } catch(ParseException e) {
+ e.printStackTrace();
+ }
- Bundle result_data = new Bundle();
- result_data.putString(REQUEST_TAG, ACTION_CHECK_CERT_VALIDITY);
- try {
- Log.d(TAG, "offset_date = " + offset_date.getTime().toString());
- certificate_x509.checkValidity(offset_date.getTime());
- mReceiver.send(Activity.RESULT_OK, result_data);
- Log.d(TAG, "Valid certificate");
- } catch(CertificateExpiredException e) {
- mReceiver.send(Activity.RESULT_CANCELED, result_data);
- Log.d(TAG, "Updating certificate");
- } catch(CertificateNotYetValidException e) {
- mReceiver.send(Activity.RESULT_CANCELED, result_data);
+ Bundle result_data = new Bundle();
+ result_data.putString(REQUEST_TAG, ACTION_CHECK_CERT_VALIDITY);
+ try {
+ Log.d(TAG, "offset_date = " + offset_date.getTime().toString());
+ certificate_x509.checkValidity(offset_date.getTime());
+ mReceiver.send(Activity.RESULT_OK, result_data);
+ Log.d(TAG, "Valid certificate");
+ } catch(CertificateExpiredException e) {
+ mReceiver.send(Activity.RESULT_CANCELED, result_data);
+ Log.d(TAG, "Updating certificate");
+ } catch(CertificateNotYetValidException e) {
+ mReceiver.send(Activity.RESULT_CANCELED, result_data);
+ }
}
}
@@ -432,31 +446,10 @@ public final class EIP extends IntentService {
this.createVPNProfile();
- setUniqueProfileName();
vpl.addProfile(mVpnProfile);
vpl.saveProfile(context, mVpnProfile);
vpl.saveProfileList(context);
}
-
-
- public String locationAsName() {
- try {
- return eipDefinition.getJSONObject("locations").getJSONObject(mGateway.getString("location")).getString("name");
- } catch (JSONException e) {
- Log.v(TAG,"Couldn't read gateway name for profile creation! Returning original name = " + mName);
- e.printStackTrace();
- return (mName != null) ? mName : "";
- }
- }
-
-
- /**
- * Attempts to create a unique profile name
- * based on the location of the gateway.
- */
- private void setUniqueProfileName() {
- mVpnProfile.mName = mName = locationAsName();
- }
/**
* Create and attach the VpnProfile to our gateway object
@@ -464,17 +457,16 @@ public final class EIP extends IntentService {
protected void createVPNProfile(){
try {
ConfigParser cp = new ConfigParser();
- Log.d(TAG, configFromEipServiceDotJson());
- Log.d(TAG, caSecretFromSharedPreferences());
- Log.d(TAG, keySecretFromSharedPreferences());
- Log.d(TAG, certSecretFromSharedPreferences());
cp.parseConfig(new StringReader(configFromEipServiceDotJson()));
cp.parseConfig(new StringReader(caSecretFromSharedPreferences()));
cp.parseConfig(new StringReader(keySecretFromSharedPreferences()));
cp.parseConfig(new StringReader(certSecretFromSharedPreferences()));
+ cp.parseConfig(new StringReader("remote-cert-tls server"));
+ cp.parseConfig(new StringReader("persist-tun"));
VpnProfile vp = cp.convertProfile();
//vp.mAuthenticationType=VpnProfile.TYPE_STATICKEYS;
mVpnProfile = vp;
+ mVpnProfile.mName = mName = locationAsName();
Log.v(TAG,"Created VPNProfile");
} catch (ConfigParseError e) {
// FIXME We didn't get a VpnProfile! Error handling! and log level
@@ -604,6 +596,16 @@ public final class EIP extends IntentService {
return secret_lines;
}
- }
+
+ public String locationAsName() {
+ try {
+ return eipDefinition.getJSONObject("locations").getJSONObject(mGateway.getString("location")).getString("name");
+ } catch (JSONException e) {
+ Log.v(TAG,"Couldn't read gateway name for profile creation! Returning original name = " + mName);
+ e.printStackTrace();
+ return (mName != null) ? mName : "";
+ }
+ }
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
index 5a5bb568..c8a28c0a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
@@ -40,10 +40,6 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
private View eipDetail;
private TextView eipStatus;
- private boolean eipAutoSwitched = true;
-
- private boolean mEipStartPending = false;
-
private static EIPReceiver mEIPReceiver;
@@ -53,29 +49,19 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- eipFragment = inflater.inflate(R.layout.eip_service_fragment, container, false);
-
+ eipFragment = inflater.inflate(R.layout.eip_service_fragment, container, false);
eipDetail = ((RelativeLayout) eipFragment.findViewById(R.id.eipDetail));
eipDetail.setVisibility(View.VISIBLE);
View eipSettings = eipFragment.findViewById(R.id.eipSettings);
eipSettings.setVisibility(View.GONE); // FIXME too!
- if (mEipStartPending)
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
+ if (EIP.mIsStarting)
+ eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
eipStatus = (TextView) eipFragment.findViewById(R.id.eipStatus);
eipSwitch = (Switch) eipFragment.findViewById(R.id.eipSwitch);
-
-
- eipSwitch.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- eipAutoSwitched = false;
- return false;
- }
- });
eipSwitch.setOnCheckedChangeListener(this);
if(getArguments() != null && getArguments().containsKey(START_ON_BOOT) && getArguments().getBoolean(START_ON_BOOT))
@@ -91,7 +77,7 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
mEIPReceiver = new EIPReceiver(new Handler());
if (savedInstanceState != null)
- mEipStartPending = savedInstanceState.getBoolean(IS_EIP_PENDING);
+ EIP.mIsStarting = savedInstanceState.getBoolean(IS_EIP_PENDING);
}
@Override
@@ -113,7 +99,7 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putBoolean(IS_EIP_PENDING, mEipStartPending);
+ outState.putBoolean(IS_EIP_PENDING, EIP.mIsStarting);
}
protected void saveEipStatus() {
@@ -124,71 +110,101 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
}
if(getActivity() != null)
- getActivity().getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).edit().putBoolean(Dashboard.START_ON_BOOT, eip_is_on).commit();
+ Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, eip_is_on).commit();
}
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (buttonView.equals(eipSwitch) && !eipAutoSwitched){
- boolean allowed_anon = getActivity().getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).getBoolean(EIP.ALLOWED_ANON, false);
- String certificate = getActivity().getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).getString(EIP.CERTIFICATE, "");
- if(allowed_anon || !certificate.isEmpty()) {
- if (isChecked){
- startEipFromScratch();
- } else {
- if (mEipStartPending){
- AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity());
- alertBuilder.setTitle(getResources().getString(R.string.eip_cancel_connect_title));
- alertBuilder
- .setMessage(getResources().getString(R.string.eip_cancel_connect_text))
- .setPositiveButton(getResources().getString(R.string.eip_cancel_connect_cancel), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- eipCommand(EIP.ACTION_STOP_EIP);
- mEipStartPending = false;
- }
- })
- .setNegativeButton(getResources().getString(R.string.eip_cancel_connect_false), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- eipAutoSwitched = true;
- eipSwitch.setChecked(true);
- eipAutoSwitched = false;
- }
- })
- .show();
- } else {
- eipCommand(EIP.ACTION_STOP_EIP);
- }
- }
- }
- else {
- Dashboard dashboard = (Dashboard)getActivity();
- Bundle waiting_on_login = new Bundle();
- waiting_on_login.putBoolean(IS_EIP_PENDING, true);
- dashboard.logInDialog(getActivity().getCurrentFocus(), waiting_on_login);
- }
- }
- else {
- if(!eipSwitch.isChecked()) {
- if(getActivity().getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).getString(EIP.STATUS, "").equalsIgnoreCase(ConnectionStatus.LEVEL_AUTH_FAILED.toString()))
- startEipFromScratch();
- else
- eipStatus.setText(R.string.state_noprocess);
- }
- }
- eipAutoSwitched = true;
- saveEipStatus();
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.equals(eipSwitch)){
+ handleSwitch(isChecked);
}
+ }
+
+ private void handleSwitch(boolean isChecked) {
+ if(isChecked)
+ handleSwitchOn();
+ else
+ handleSwitchOff();
+ saveEipStatus();
+ }
+
+ private void handleSwitchOn() {
+ if(canStartEIP())
+ startEipFromScratch();
+ else if(canLogInToStartEIP()) {
+ Log.d(TAG, "Can Log In to start EIP");
+ Dashboard dashboard = (Dashboard) getActivity();
+ dashboard.logInDialog(Bundle.EMPTY);
+ }
+ }
+
+ private boolean canStartEIP() {
+ boolean certificateExists = !Dashboard.preferences.getString(EIP.CERTIFICATE, "").isEmpty();
+ boolean isAllowedAnon = Dashboard.preferences.getBoolean(EIP.ALLOWED_ANON, false);
+ return (isAllowedAnon || certificateExists) && !EIP.mIsStarting && !EIP.isConnected();
+ }
+
+ private boolean canLogInToStartEIP() {
+ boolean isAllowedRegistered = Dashboard.preferences.getBoolean(EIP.ALLOWED_REGISTERED, false);
+ boolean isLoggedIn = Dashboard.preferences.getBoolean(EIP.AUTHED_EIP, false);
+ Log.d(TAG, "Allow registered? " + isAllowedRegistered);
+ Log.d(TAG, "Is logged in? " + isLoggedIn);
+ return isAllowedRegistered && !isLoggedIn && !EIP.mIsStarting && !EIP.isConnected();
+ }
+
+ private void handleSwitchOff() {
+ if(EIP.mIsStarting) {
+ askPendingStartCancellation();
+ } else if(EIP.isConnected()) {
+ Log.d(TAG, "Stopping EIP");
+ stopEIP();
+ }
+ }
+
+ private void askPendingStartCancellation() {
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity());
+ alertBuilder.setTitle(getResources().getString(R.string.eip_cancel_connect_title))
+ .setMessage(getResources().getString(R.string.eip_cancel_connect_text))
+ .setPositiveButton((R.string.yes), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ stopEIP();
+ }
+ })
+ .setNegativeButton(getResources().getString(R.string.no), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Log.d(TAG, "askPendingStartCancellation checks the switch to true");
+ eipSwitch.setChecked(true);
+ }
+ })
+ .show();
+ }
public void startEipFromScratch() {
- mEipStartPending = true;
+ EIP.mIsStarting = true;
eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
- ((TextView) eipFragment.findViewById(R.id.eipStatus)).setText(R.string.eip_status_start_pending);
- eipSwitch.setChecked(true);
- saveEipStatus();
+ String status = getResources().getString(R.string.eip_status_start_pending);
+ setEipStatus(status);
+
+ if(!eipSwitch.isChecked()) {
+ Log.d(TAG, "startEipFromScratch checks the switch to true");
+ eipSwitch.setChecked(true);
+ saveEipStatus();
+ }
eipCommand(EIP.ACTION_START_EIP);
}
+
+ private void stopEIP() {
+ EIP.mIsStarting = false;
+ View eipProgressBar = getActivity().findViewById(R.id.eipProgress);
+ if(eipProgressBar != null)
+ eipProgressBar.setVisibility(View.GONE);
+
+ String status = getResources().getString(R.string.eip_state_not_connected);
+ setEipStatus(status);
+ eipCommand(EIP.ACTION_STOP_EIP);
+ }
/**
* Send a command to EIP
@@ -204,44 +220,98 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
getActivity().startService(vpn_intent);
}
- @Override
- public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
- // Note: "states" are not organized anywhere...collected state strings:
- // NOPROCESS,NONETWORK,BYTECOUNT,AUTH_FAILED + some parsing thing ( WAIT(?),AUTH,GET_CONFIG,ASSIGN_IP,CONNECTED,SIGINT )
- getActivity().runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- if (eipStatus != null) {
- boolean switchState = true;
- String statusMessage = "";
- String prefix = getString(localizedResId);
- if (level == ConnectionStatus.LEVEL_CONNECTED){
- statusMessage = getString(R.string.eip_state_connected);
- getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE);
- mEipStartPending = false;
- } else if ( level == ConnectionStatus.LEVEL_NONETWORK || level == ConnectionStatus.LEVEL_NOTCONNECTED || level == ConnectionStatus.LEVEL_AUTH_FAILED) {
- statusMessage = getString(R.string.eip_state_not_connected);
- if(getActivity() != null && getActivity().findViewById(R.id.eipProgress) != null)
- getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE);
- mEipStartPending = false;
- switchState = false;
- } else if (level == ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED) {
- if(state.equals("AUTH") || state.equals("GET_CONFIG"))
- statusMessage = prefix + " " + logmessage;
- } else if (level == ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET) {
- statusMessage = prefix + " " + logmessage;
- }
-
- eipAutoSwitched = true;
- eipSwitch.setChecked(switchState);
- eipAutoSwitched = false;
- eipStatus.setText(statusMessage);
- }
- }
+ @Override
+ public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
+ boolean isNewLevel = EIP.lastConnectionStatusLevel != level;
+ boolean justDecidedOnDisconnect = EIP.lastConnectionStatusLevel == ConnectionStatus.UNKNOWN_LEVEL;
+ Log.d(TAG, "update state with level " + level);
+ if(!justDecidedOnDisconnect && (isNewLevel || level == ConnectionStatus.LEVEL_CONNECTED)) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ EIP.lastConnectionStatusLevel = level;
+ handleNewState(state, logmessage, localizedResId, level);
+ }
});
+ } else if(justDecidedOnDisconnect && level == ConnectionStatus.LEVEL_CONNECTED) {
+ EIP.lastConnectionStatusLevel = ConnectionStatus.LEVEL_NOTCONNECTED;
+ updateState(state, logmessage, localizedResId, level);
}
+ }
+
+ private void handleNewState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
+ if (level == ConnectionStatus.LEVEL_CONNECTED)
+ setConnectedUI();
+ else if (isDisconnectedLevel(level) && !EIP.mIsStarting)
+ setDisconnectedUI();
+ else if (level == ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET)
+ setNoServerReplyUI(localizedResId, logmessage);
+ else if (level == ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED)
+ setServerReplyUI(state, localizedResId, logmessage);
+ }
+
+ private boolean isDisconnectedLevel(final ConnectionStatus level) {
+ return level == ConnectionStatus.LEVEL_NONETWORK || level == ConnectionStatus.LEVEL_NOTCONNECTED || level == ConnectionStatus.LEVEL_AUTH_FAILED;
+ }
+
+ private void setConnectedUI() {
+ hideProgressBar();
+ Log.d(TAG, "mIsDisconnecting = false in setConnectedUI");
+ EIP.mIsStarting = false; //TODO This should be done in the onReceiveResult from START_EIP command, but right now LaunchVPN isn't notifying anybody the resultcode of the request so we need to listen the states with this listener.
+ EIP.mIsDisconnecting = false; //TODO See comment above
+ String status = getString(R.string.eip_state_connected);
+ setEipStatus(status);
+ adjustSwitch();
+ }
+ private void setDisconnectedUI(){
+ hideProgressBar();
+ EIP.mIsStarting = false; //TODO See comment in setConnectedUI()
+ Log.d(TAG, "mIsDisconnecting = false in setDisconnectedUI");
+ EIP.mIsDisconnecting = false; //TODO See comment in setConnectedUI()
+
+ String status = getString(R.string.eip_state_not_connected);
+ setEipStatus(status);
+ adjustSwitch();
+ }
+
+ private void adjustSwitch() {
+ if(EIP.isConnected()) {
+ if(!eipSwitch.isChecked()) {
+ eipSwitch.setChecked(true);
+ }
+ } else {
+ if(eipSwitch.isChecked()) {
+ eipSwitch.setChecked(false);
+ }
+ }
+ }
+
+ private void setNoServerReplyUI(int localizedResId, String logmessage) {
+ if(eipStatus != null) {
+ String prefix = getString(localizedResId);
+ setEipStatus(prefix + " " + logmessage);
+ }
+ }
+
+ private void setServerReplyUI(String state, int localizedResId, String logmessage) {
+ if(eipStatus != null)
+ if(state.equals("AUTH") || state.equals("GET_CONFIG")) {
+ String prefix = getString(localizedResId);
+ setEipStatus(prefix + " " + logmessage);
+ }
+ }
+
+ protected void setEipStatus(String status) {
+ if(eipStatus == null)
+ eipStatus = (TextView) getActivity().findViewById(R.id.eipStatus);
+ eipStatus.setText(status);
+ }
+
+ private void hideProgressBar() {
+ if(getActivity() != null && getActivity().findViewById(R.id.eipProgress) != null)
+ getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE);
+ }
/**
* Inner class for handling messages related to EIP status and control requests
@@ -273,8 +343,10 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
} else if (request == EIP.ACTION_START_EIP) {
switch (resultCode){
case Activity.RESULT_OK:
+ Log.d(TAG, "Action start eip = Result OK");
checked = true;
eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
+ EIP.mIsStarting = false;
break;
case Activity.RESULT_CANCELED:
checked = false;
@@ -307,9 +379,10 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
break;
case Activity.RESULT_CANCELED:
Dashboard dashboard = (Dashboard) getActivity();
-
+
dashboard.setProgressBarVisibility(ProgressBar.VISIBLE);
- dashboard.setEipStatus(R.string.updating_certificate_message);
+ String status = getResources().getString(R.string.updating_certificate_message);
+ setEipStatus(status);
Intent provider_API_command = new Intent(getActivity(), ProviderAPI.class);
if(dashboard.providerAPI_result_receiver == null) {
@@ -323,10 +396,6 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
break;
}
}
-
- eipAutoSwitched = true;
- eipSwitch.setChecked(checked);
- eipAutoSwitched = false;
}
}
@@ -341,6 +410,7 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
public void checkEipSwitch(boolean checked) {
eipSwitch.setChecked(checked);
- onCheckedChanged(eipSwitch, checked);
+ // Log.d(TAG, "checkEipSwitch");
+ // onCheckedChanged(eipSwitch, checked);
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipStatusReceiver.java b/app/src/main/java/se/leap/bitmaskclient/EipStatusReceiver.java
deleted file mode 100644
index 8793cf36..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/EipStatusReceiver.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package se.leap.bitmaskclient;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-
-public class EipStatusReceiver extends BroadcastReceiver {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals("de.blinkt.openvpn.VPN_STATUS")) {
- context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE).edit().putString(EIP.STATUS, intent.getStringExtra("status")).commit();
- }
- }
-}