summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/base
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/base')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java84
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java86
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/MotdFragment.java103
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java7
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java152
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/DateHelper.java11
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java70
8 files changed, 438 insertions, 79 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
index 8cb12652..e2fa0783 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
@@ -17,6 +17,34 @@
package se.leap.bitmaskclient.base;
+import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed;
+import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message;
+import static se.leap.bitmaskclient.base.models.Constants.ASK_TO_CANCEL_VPN;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_CODE;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_LAUNCH_VPN;
+import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_PREPARE_VPN;
+import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START;
+import static se.leap.bitmaskclient.base.models.Constants.EIP_REQUEST;
+import static se.leap.bitmaskclient.base.models.Constants.EXTRA_MOTD_MSG;
+import static se.leap.bitmaskclient.base.models.Constants.LOCATION;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE_LEAP;
+import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_LOG_IN;
+import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_SWITCH_PROVIDER;
+import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.storeProviderInPreferences;
+import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_INVALID_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_VPN_PREPARE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORID;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.TOR_EXCEPTION;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.TOR_TIMEOUT;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE;
+
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
@@ -40,6 +68,7 @@ import se.leap.bitmaskclient.base.fragments.EipFragment;
import se.leap.bitmaskclient.base.fragments.ExcludeAppsFragment;
import se.leap.bitmaskclient.base.fragments.LogFragment;
import se.leap.bitmaskclient.base.fragments.MainActivityErrorDialog;
+import se.leap.bitmaskclient.base.fragments.MotdFragment;
import se.leap.bitmaskclient.base.fragments.NavigationDrawerFragment;
import se.leap.bitmaskclient.base.fragments.SettingsFragment;
import se.leap.bitmaskclient.base.models.Provider;
@@ -49,38 +78,10 @@ import se.leap.bitmaskclient.eip.EIP;
import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.eip.EipSetupListener;
import se.leap.bitmaskclient.eip.EipSetupObserver;
-import se.leap.bitmaskclient.eip.EipStatus;
import se.leap.bitmaskclient.providersetup.ProviderAPI;
import se.leap.bitmaskclient.providersetup.activities.LoginActivity;
import se.leap.bitmaskclient.providersetup.models.LeapSRPSession;
-import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed;
-import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message;
-import static se.leap.bitmaskclient.base.models.Constants.ASK_TO_CANCEL_VPN;
-import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_CODE;
-import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
-import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_LAUNCH_VPN;
-import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_PREPARE_VPN;
-import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START;
-import static se.leap.bitmaskclient.base.models.Constants.EIP_REQUEST;
-import static se.leap.bitmaskclient.base.models.Constants.LOCATION;
-import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
-import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE_LEAP;
-import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_LOG_IN;
-import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_SWITCH_PROVIDER;
-import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.storeProviderInPreferences;
-import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_INVALID_VPN_CERTIFICATE;
-import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_VPN_PREPARE;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORID;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.TOR_EXCEPTION;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.TOR_TIMEOUT;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE;
-
public class MainActivity extends AppCompatActivity implements EipSetupListener, Observer {
@@ -93,6 +94,7 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
public final static String ACTION_SHOW_VPN_FRAGMENT = "action_show_vpn_fragment";
public final static String ACTION_SHOW_LOG_FRAGMENT = "action_show_log_fragment";
public final static String ACTION_SHOW_DIALOG_FRAGMENT = "action_show_dialog_fragment";
+ public final static String ACTION_SHOW_MOTD_FRAGMENT = "action_show_motd_fragment";
/**
* Fragment managing the behaviors, interactions and presentation of the navigation drawer.
@@ -161,10 +163,22 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
bundle.putParcelable(PROVIDER_KEY, provider);
fragment.setArguments(bundle);
hideActionBarSubTitle();
+ showActionBar();
+ break;
+ case ACTION_SHOW_MOTD_FRAGMENT:
+ fragment = new MotdFragment();
+ Bundle motdBundle = new Bundle();
+ if (intent.hasExtra(EXTRA_MOTD_MSG)) {
+ motdBundle.putString(EXTRA_MOTD_MSG, intent.getStringExtra(EXTRA_MOTD_MSG));
+ }
+ fragment.setArguments(motdBundle);
+ hideActionBarSubTitle();
+ hideActionBar();
break;
case ACTION_SHOW_LOG_FRAGMENT:
fragment = new LogFragment();
setActionBarTitle(R.string.log_fragment_title);
+ showActionBar();
break;
case ACTION_SHOW_DIALOG_FRAGMENT:
if (intent.hasExtra(EIP.ERRORID)) {
@@ -186,6 +200,20 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
}
}
+ private void hideActionBar() {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.hide();
+ }
+ }
+
+ private void showActionBar() {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.show();
+ }
+ }
+
private void hideActionBarSubTitle() {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java
index 8d11105f..6e557236 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java
@@ -16,6 +16,19 @@
*/
package se.leap.bitmaskclient.base;
+import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_MOTD_FRAGMENT;
+import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
+import static se.leap.bitmaskclient.base.models.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE;
+import static se.leap.bitmaskclient.base.models.Constants.EIP_RESTART_ON_BOOT;
+import static se.leap.bitmaskclient.base.models.Constants.EXTRA_MOTD_MSG;
+import static se.leap.bitmaskclient.base.models.Constants.PREFERENCES_APP_VERSION;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_EIP_DEFINITION;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE_LEAP;
+import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.isDefaultBitmask;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.storeProviderInPreferences;
+
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -28,27 +41,22 @@ import androidx.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
import de.blinkt.openvpn.core.VpnStatus;
+import motd.IMessage;
+import motd.IMessages;
+import motd.IStringCollection;
+import motd.Motd;
import se.leap.bitmaskclient.BuildConfig;
-import se.leap.bitmaskclient.providersetup.ProviderListActivity;
-import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.base.models.FeatureVersionCode;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
-import se.leap.bitmaskclient.providersetup.activities.CustomProviderSetupActivity;
+import se.leap.bitmaskclient.base.utils.DateHelper;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
-
-import static se.leap.bitmaskclient.base.models.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE;
-import static se.leap.bitmaskclient.base.models.Constants.EIP_RESTART_ON_BOOT;
-import static se.leap.bitmaskclient.base.models.Constants.PREFERENCES_APP_VERSION;
-import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_EIP_DEFINITION;
-import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
-import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE_LEAP;
-import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
-import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.isDefaultBitmask;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.storeProviderInPreferences;
+import se.leap.bitmaskclient.eip.EipCommand;
+import se.leap.bitmaskclient.providersetup.ProviderListActivity;
+import se.leap.bitmaskclient.providersetup.activities.CustomProviderSetupActivity;
/**
* Activity shown at startup. Evaluates if App is started for the first time or has been upgraded
@@ -202,12 +210,13 @@ public class StartActivity extends Activity{
EipCommand.startVPN(this, true);
finish();
} else if (PreferenceHelper.getRestartOnUpdate(this.getApplicationContext())) {
+ // This is relevant for web build flavor apks
PreferenceHelper.restartOnUpdate(this.getApplicationContext(), false);
EipCommand.startVPN(this, false);
- showMainActivity();
+ showNextActivity(provider);
finish();
} else {
- showMainActivity();
+ showNextActivity(provider);
}
} else {
configureLeapProvider();
@@ -234,18 +243,59 @@ public class StartActivity extends Activity{
storeProviderInPreferences(preferences, provider);
ProviderObservable.getInstance().updateProvider(provider);
EipCommand.startVPN(this, false);
- showMainActivity();
+ showNextActivity(provider);
} else if (resultCode == RESULT_CANCELED) {
finish();
}
}
}
- private void showMainActivity() {
+ private void showNextActivity(Provider provider) {
+ if (provider.shouldShowMotdSeen()) {
+ try {
+ IMessages messages = Motd.newMessages(provider.getMotdJsonString());
+
+ IStringCollection stringCollection = Motd.newStringCollection(); //provider.getMotdLastSeenHashCollection();
+ String formattedDate = DateHelper.getFormattedDateWithTimezone(provider.getLastMotdSeen());
+ IMessage message = messages.getFirstMessage(formattedDate, stringCollection);
+ if (message != null) {
+ IMessage imessage = Motd.newMessage(message.toJson());
+ showMotd(provider, imessage);
+ return;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Couldn't show Motd. Invalid timestamp.");
+ }
+ }
+ showVPNFragment();
+ }
+
+ private void showMotd(Provider p, IMessage message) {
+ if (message.mType().equals(Motd.MESSAGE_TYPE_ONCE)) {
+ Set<String> lastSeenHashes = p.getMotdLastSeenHashes();
+ String hash = message.hash();
+ lastSeenHashes.add(hash);
+ p.setMotdLastSeenHashes(lastSeenHashes);
+ PreferenceHelper.persistProvider(this, p);
+ ProviderObservable.getInstance().updateProvider(p);
+ }
+ showMotdFragment(message);
+ }
+
+ private void showVPNFragment() {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(ACTION_SHOW_VPN_FRAGMENT);
startActivity(intent);
finish();
}
+
+ private void showMotdFragment(IMessage message) {
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.setAction(ACTION_SHOW_MOTD_FRAGMENT);
+ intent.putExtra(EXTRA_MOTD_MSG, message.toJson());
+ startActivity(intent);
+ finish();
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/MotdFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/MotdFragment.java
new file mode 100644
index 00000000..16834ab5
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/MotdFragment.java
@@ -0,0 +1,103 @@
+package se.leap.bitmaskclient.base.fragments;
+
+import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
+import static se.leap.bitmaskclient.base.models.Constants.EXTRA_MOTD_MSG;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Html;
+import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatImageButton;
+import androidx.appcompat.widget.AppCompatTextView;
+import androidx.fragment.app.Fragment;
+
+import java.util.Locale;
+
+import de.blinkt.openvpn.core.VpnStatus;
+import motd.IMessage;
+import motd.Motd;
+import se.leap.bitmaskclient.base.MainActivity;
+import se.leap.bitmaskclient.databinding.FMotdBinding;
+
+
+public class MotdFragment extends Fragment {
+
+ private static final String TAG = MotdFragment.class.getSimpleName();
+ private IMessage message;
+ FMotdBinding binding;
+ AppCompatTextView messageView;
+ AppCompatImageButton nextButton;
+
+ public MotdFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments() != null) {
+ String messageString = getArguments().getString(EXTRA_MOTD_MSG);
+ if (messageString != null) {
+ Log.d(TAG, "MotdFragment received: " + messageString);
+ message = Motd.newMessage(messageString);
+ }
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ binding = FMotdBinding.inflate(getLayoutInflater());
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ messageView = binding.motdContent;
+ nextButton = binding.nextBtn;
+ String currentLang = Locale.getDefault().getLanguage();
+ String text = message.getLocalizedText(currentLang);
+ if (TextUtils.isEmpty(text)) {
+ text = message.getLocalizedText("en");
+ }
+
+ if (TextUtils.isEmpty(text)) {
+ String error = "Message of the day cannot be shown. Unsupported app language and unknown default langauge.";
+ Log.e(TAG, error);
+ VpnStatus.logError(error);
+ showVpnFragment(view.getContext());
+ return;
+ }
+
+ Log.d(TAG, "set motd text: " + text);
+ messageView.setText(Html.fromHtml(text));
+ messageView.setMovementMethod(LinkMovementMethod.getInstance());
+ nextButton.setOnClickListener(v -> {
+ showVpnFragment(v.getContext());
+ });
+
+ }
+
+ private void showVpnFragment(Context context) {
+ try {
+ Intent intent = new Intent(context, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.setAction(ACTION_SHOW_VPN_FRAGMENT);
+ context.startActivity(intent);
+ } catch (NullPointerException npe) {
+ npe.printStackTrace();
+ }
+
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java
index 6829d9f1..2c0fdd69 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java
@@ -7,7 +7,6 @@ import android.app.Dialog;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
-import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -15,9 +14,6 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.appcompat.widget.AppCompatButton;
import androidx.appcompat.widget.AppCompatEditText;
-import androidx.appcompat.widget.AppCompatSpinner;
-
-import java.util.ArrayList;
import se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
index fd9f2a9b..ee5bd2a7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
@@ -53,7 +53,7 @@ public interface Constants {
String OBFUSCATION_PINNING_LOCATION = "obfuscation_pinning_location";
- //////////////////////////////////////////////
+ //////////////////////////////////////////////
// REQUEST CODE CONSTANTS
/////////////////////////////////////////////
@@ -77,6 +77,7 @@ public interface Constants {
String ASK_TO_CANCEL_VPN = "ask_to_cancel_vpn";
+ String EXTRA_MOTD_MSG = "extra_motd_message";
//////////////////////////////////////////////
@@ -114,6 +115,10 @@ public interface Constants {
String PROVIDER_EIP_DEFINITION = "Constants.EIP_DEFINITION";
String PROVIDER_PROFILE_UUID = "Constants.PROVIDER_PROFILE_UUID";
String PROVIDER_PROFILE = "Constants.PROVIDER_PROFILE";
+ String PROVIDER_MOTD = "Constants.PROVIDER_MOTD";
+ String PROVIDER_MOTD_HASHES = "Constants.PROVIDER_MOTD_HASHES";
+ String PROVIDER_MOTD_LAST_SEEN = "Constants.PROVIDER_MOTD_LAST_SEEN";
+ String PROVIDER_MOTD_LAST_UPDATED = "Constants.PROVIDER_MOTD_LAST_UPDATED";
////////////////////////////////////////////////
// PRESHIPPED PROVIDER CONFIG
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java
index 13463167..53f864cf 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java
@@ -16,9 +16,23 @@
*/
package se.leap.bitmaskclient.base.models;
+import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4;
+import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_KCP;
+import static se.leap.bitmaskclient.base.models.Constants.CAPABILITIES;
+import static se.leap.bitmaskclient.base.models.Constants.GATEWAYS;
+import static se.leap.bitmaskclient.base.models.Constants.LOCATIONS;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOWED_REGISTERED;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOW_ANONYMOUS;
+import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT;
+import static se.leap.bitmaskclient.base.models.Constants.TYPE;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
+
import android.os.Parcel;
import android.os.Parcelable;
+import androidx.annotation.NonNull;
+
import com.google.gson.Gson;
import org.json.JSONArray;
@@ -27,22 +41,14 @@ import org.json.JSONObject;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Locale;
+import java.util.Set;
-import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4;
-import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_KCP;
-import static se.leap.bitmaskclient.base.models.Constants.CAPABILITIES;
-import static se.leap.bitmaskclient.base.models.Constants.GATEWAYS;
-import static se.leap.bitmaskclient.base.models.Constants.LOCATIONS;
-import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOWED_REGISTERED;
-import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOW_ANONYMOUS;
-import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT;
-import static se.leap.bitmaskclient.base.models.Constants.TYPE;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
-
-import de.blinkt.openvpn.core.connection.Connection;
import de.blinkt.openvpn.core.connection.Connection.TransportType;
+import motd.IStringCollection;
+import motd.Motd;
/**
* @author Sean Leonard <meanderingcode@aetherislands.net>
@@ -50,14 +56,17 @@ import de.blinkt.openvpn.core.connection.Connection.TransportType;
*/
public final class Provider implements Parcelable {
- private static long EIP_SERVICE_TIMEOUT = 1000 * 60 * 60 * 24 * 3;
- private static long GEOIP_SERVICE_TIMEOUT = 1000 * 60 * 60;
+ private static final long EIP_SERVICE_TIMEOUT = 1000 * 60 * 60 * 24 * 3;
+ private static final long GEOIP_SERVICE_TIMEOUT = 1000 * 60 * 60;
+ private static final long MOTD_TIMEOUT = 1000 * 60 * 60 * 24;
private JSONObject definition = new JSONObject(); // Represents our Provider's provider.json
private JSONObject eipServiceJson = new JSONObject();
private JSONObject geoIpJson = new JSONObject();
+ private JSONObject motdJson = new JSONObject();
private DefaultedURL mainUrl = new DefaultedURL();
private DefaultedURL apiUrl = new DefaultedURL();
private DefaultedURL geoipUrl = new DefaultedURL();
+ private DefaultedURL motdUrl = new DefaultedURL();
private String domain = "";
private String providerIp = ""; // ip of the provider main url
private String providerApiIp = ""; // ip of the provider api url
@@ -69,6 +78,9 @@ public final class Provider implements Parcelable {
private String vpnCertificate = "";
private long lastEipServiceUpdate = 0L;
private long lastGeoIpUpdate = 0L;
+ private long lastMotdUpdate = 0L;
+ private long lastMotdSeen = 0L;
+ private Set<String> lastMotdSeenHashes = new HashSet<>();
private boolean shouldUpdateVpnCertificate;
private boolean allowAnonymous;
@@ -90,7 +102,8 @@ public final class Provider implements Parcelable {
MAIN_URL = "main_url",
PROVIDER_IP = "provider_ip",
PROVIDER_API_IP = "provider_api_ip",
- GEOIP_URL = "geoip_url";
+ GEOIP_URL = "geoip_url",
+ MOTD_URL = "motd_url";
private static final String API_TERM_NAME = "name";
@@ -110,10 +123,10 @@ public final class Provider implements Parcelable {
}
public Provider(String mainUrl, String providerIp, String providerApiIp) {
- this(mainUrl, null, providerIp, providerApiIp);
+ this(mainUrl, null, null, providerIp, providerApiIp);
}
- public Provider(String mainUrl, String geoipUrl, String providerIp, String providerApiIp) {
+ public Provider(String mainUrl, String geoipUrl, String motdUrl, String providerIp, String providerApiIp) {
try {
this.mainUrl.setUrl(new URL(mainUrl));
if (providerIp != null) {
@@ -127,11 +140,11 @@ public final class Provider implements Parcelable {
return;
}
setGeoipUrl(geoipUrl);
+ setMotdUrl(motdUrl);
}
-
- public Provider(String mainUrl, String geoipUrl, String providerIp, String providerApiIp, String caCert, String definition) {
- this(mainUrl, geoipUrl, providerIp, providerApiIp);
+ public Provider(String mainUrl, String geoipUrl, String motdUrl, String providerIp, String providerApiIp, String caCert, String definition) {
+ this(mainUrl, geoipUrl, motdUrl, providerIp, providerApiIp);
if (caCert != null) {
this.caCert = caCert;
}
@@ -281,6 +294,18 @@ public final class Provider implements Parcelable {
}
}
+ public DefaultedURL getMotdUrl() {
+ return this.motdUrl;
+ }
+
+ public void setMotdUrl(String url) {
+ try {
+ this.motdUrl.setUrl(new URL(url));
+ } catch (MalformedURLException e) {
+ this.motdUrl = new DefaultedURL();
+ }
+ }
+
public String getApiUrlWithVersion() {
return getApiUrlString() + "/" + getApiVersion();
}
@@ -375,14 +400,19 @@ public final class Provider implements Parcelable {
parcel.writeString(getProviderIp());
parcel.writeString(getProviderApiIp());
parcel.writeString(getGeoipUrl().toString());
+ parcel.writeString(getMotdUrl().toString());
parcel.writeString(getDefinitionString());
parcel.writeString(getCaCert());
parcel.writeString(getEipServiceJsonString());
parcel.writeString(getGeoIpJsonString());
+ parcel.writeString(getMotdJsonString());
parcel.writeString(getPrivateKey());
parcel.writeString(getVpnCertificate());
parcel.writeLong(lastEipServiceUpdate);
parcel.writeLong(lastGeoIpUpdate);
+ parcel.writeLong(lastMotdUpdate);
+ parcel.writeLong(lastMotdSeen);
+ parcel.writeStringList(new ArrayList<>(lastMotdSeenHashes));
parcel.writeInt(shouldUpdateVpnCertificate ? 0 : 1);
}
@@ -406,6 +436,10 @@ public final class Provider implements Parcelable {
}
tmpString = in.readString();
if (!tmpString.isEmpty()) {
+ motdUrl.setUrl(new URL(tmpString));
+ }
+ tmpString = in.readString();
+ if (!tmpString.isEmpty()) {
definition = new JSONObject((tmpString));
parseDefinition(definition);
}
@@ -423,6 +457,10 @@ public final class Provider implements Parcelable {
}
tmpString = in.readString();
if (!tmpString.isEmpty()) {
+ this.setMotdJson(new JSONObject(tmpString));
+ }
+ tmpString = in.readString();
+ if (!tmpString.isEmpty()) {
this.setPrivateKey(tmpString);
}
tmpString = in.readString();
@@ -431,6 +469,11 @@ public final class Provider implements Parcelable {
}
this.lastEipServiceUpdate = in.readLong();
this.lastGeoIpUpdate = in.readLong();
+ this.lastMotdUpdate = in.readLong();
+ this.lastMotdSeen = in.readLong();
+ ArrayList<String> lastMotdSeenHashes = new ArrayList<>();
+ in.readStringList(lastMotdSeenHashes);
+ this.lastMotdSeenHashes = new HashSet<>(lastMotdSeenHashes);
this.shouldUpdateVpnCertificate = in.readInt() == 0;
} catch (MalformedURLException | JSONException e) {
e.printStackTrace();
@@ -447,10 +490,12 @@ public final class Provider implements Parcelable {
definition.toString().equals(p.getDefinition().toString()) &&
eipServiceJson.toString().equals(p.getEipServiceJsonString()) &&
geoIpJson.toString().equals(p.getGeoIpJsonString()) &&
+ motdJson.toString().equals(p.getMotdJsonString()) &&
providerIp.equals(p.getProviderIp()) &&
providerApiIp.equals(p.getProviderApiIp()) &&
apiUrl.equals(p.getApiUrl()) &&
geoipUrl.equals(p.getGeoipUrl()) &&
+ motdUrl.equals(p.getMotdUrl()) &&
certificatePin.equals(p.getCertificatePin()) &&
certificatePinEncoding.equals(p.getCertificatePinEncoding()) &&
caCert.equals(p.getCaCert()) &&
@@ -527,6 +572,70 @@ public final class Provider implements Parcelable {
return shouldUpdateVpnCertificate;
}
+ public void setLastMotdSeen(long timestamp) {
+ lastMotdSeen = timestamp;
+ }
+
+ public long getLastMotdSeen() {
+ return lastMotdSeen;
+ }
+
+ /**
+ * shouldShowMotdSeen
+ * @return true if last message of the day was shown more than 24h ago
+ */
+ public boolean shouldShowMotdSeen() {
+ return !motdUrl.isDefault() && System.currentTimeMillis() - lastMotdSeen >= MOTD_TIMEOUT;
+ }
+
+ /**
+ * setLastSeenHashes
+ * @param hashes hashes of messages of type 'once' that have already been seen
+ */
+ public void setMotdLastSeenHashes(Set<String> hashes) {
+ lastMotdSeenHashes = hashes;
+ }
+
+ public Set<String> getMotdLastSeenHashes() {
+ return lastMotdSeenHashes;
+ }
+
+ /**
+ * getLastSeenHashCollection
+ * @return go ffi compatible IStringCollection interface of message hashes of type 'once'
+ */
+ public IStringCollection getMotdLastSeenHashCollection() {
+ IStringCollection stringCollection = Motd.newStringCollection();
+ for (String hash : lastMotdSeenHashes) {
+ stringCollection.add(hash);
+ }
+ return stringCollection;
+ }
+
+ public void setLastMotdUpdate(long timestamp) {
+ lastMotdUpdate = timestamp;
+ }
+
+ public long getLastMotdUpdate() {
+ return lastMotdUpdate;
+ }
+
+ public boolean shouldUpdateMotdJson() {
+ return !motdUrl.isDefault() && System.currentTimeMillis() - lastMotdUpdate >= MOTD_TIMEOUT;
+ }
+
+ public void setMotdJson(@NonNull JSONObject motdJson) {
+ this.motdJson = motdJson;
+ }
+
+ public JSONObject getMotdJson() {
+ return motdJson;
+ }
+
+ public String getMotdJsonString() {
+ return motdJson.toString();
+ }
+
public void setLastGeoIpUpdate(long timestamp) {
lastGeoIpUpdate = timestamp;
}
@@ -621,6 +730,7 @@ public final class Provider implements Parcelable {
definition = new JSONObject();
eipServiceJson = new JSONObject();
geoIpJson = new JSONObject();
+ motdJson = new JSONObject();
apiUrl = new DefaultedURL();
certificatePin = "";
certificatePinEncoding = "";
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/DateHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/DateHelper.java
index 0476bf12..2e9c2b24 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/DateHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/DateHelper.java
@@ -12,6 +12,7 @@ import java.util.Locale;
*/
public class DateHelper {
private static final String DATE_PATTERN = "dd/MM/yyyy";
+ private static final String DATE_PATTERN_RFC822_NUMERIC_ZONE = "dd MMM yy hh:mm Z"; // RFC822 with numeric zone
private static final int ONE_DAY = 86400000; //1000*60*60*24
public static long getDateDiffToCurrentDateInDays(String startDate) throws ParseException {
@@ -26,4 +27,14 @@ public class DateHelper {
Date lastDate = new Date();
return sdf.format(lastDate);
}
+
+ /**
+ * Return date in a specific format bitmaskcore can handle.
+ * @param milliSeconds Date in milliseconds
+ * @return String representing date in specified format
+ */
+ public static String getFormattedDateWithTimezone(long milliSeconds) throws IllegalArgumentException {
+ SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN_RFC822_NUMERIC_ZONE, Locale.US);
+ return sdf.format(milliSeconds);
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
index 4abeed1a..76e54794 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
@@ -20,6 +20,10 @@ import static se.leap.bitmaskclient.base.models.Constants.PREFERRED_CITY;
import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_CONFIGURED;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_EIP_DEFINITION;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD_HASHES;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD_LAST_SEEN;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD_LAST_UPDATED;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PRIVATE_KEY;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.base.models.Constants.RESTART_ON_UPDATE;
@@ -62,11 +66,16 @@ public class PreferenceHelper {
provider.setProviderIp(preferences.getString(Provider.PROVIDER_IP, ""));
provider.setProviderApiIp(preferences.getString(Provider.PROVIDER_API_IP, ""));
provider.setGeoipUrl(preferences.getString(Provider.GEOIP_URL, ""));
+ provider.setMotdUrl(preferences.getString(Provider.MOTD_URL, ""));
provider.define(new JSONObject(preferences.getString(Provider.KEY, "")));
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, "")));
+ provider.setMotdJson(new JSONObject(preferences.getString(PROVIDER_MOTD, "")));
+ provider.setLastMotdSeen(preferences.getLong(PROVIDER_MOTD_LAST_SEEN, 0L));
+ provider.setLastMotdUpdate(preferences.getLong(PROVIDER_MOTD_LAST_UPDATED, 0L));
+ provider.setMotdLastSeenHashes(preferences.getStringSet(PROVIDER_MOTD_HASHES, new HashSet<>()));
} catch (MalformedURLException | JSONException e) {
e.printStackTrace();
}
@@ -78,12 +87,31 @@ public class PreferenceHelper {
return preferences.getString(toFetch + "." + providerDomain, "");
}
+ public static long getLongFromPersistedProvider(String toFetch, String providerDomain, SharedPreferences preferences) {
+ return preferences.getLong(toFetch + "." + providerDomain, 0L);
+ }
+
+ public static Set<String> getStringSetFromPersistedProvider(String toFetch, String providerDomain, SharedPreferences preferences) {
+ return preferences.getStringSet(toFetch + "." + providerDomain, new HashSet<>());
+ }
+
+ public static void persistProvider(Context context, Provider provider) {
+ SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+ storeProviderInPreferences(preferences, provider, true);
+ }
+
+ public static void storeProviderInPreferences(SharedPreferences preferences, Provider provider) {
+ storeProviderInPreferences(preferences, provider, false);
+ }
+
// TODO: replace commit with apply after refactoring EIP
//FIXME: don't save private keys in shared preferences! use the keystore
- public static void storeProviderInPreferences(SharedPreferences preferences, Provider provider) {
- preferences.edit().putBoolean(PROVIDER_CONFIGURED, true).
+ public static void storeProviderInPreferences(SharedPreferences preferences, Provider provider, boolean apply) {
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putBoolean(PROVIDER_CONFIGURED, true).
putString(Provider.PROVIDER_IP, provider.getProviderIp()).
putString(Provider.GEOIP_URL, provider.getGeoipUrl().toString()).
+ putString(Provider.MOTD_URL, provider.getMotdUrl().toString()).
putString(Provider.PROVIDER_API_IP, provider.getProviderApiIp()).
putString(Provider.MAIN_URL, provider.getMainUrlString()).
putString(Provider.KEY, provider.getDefinitionString()).
@@ -91,7 +119,15 @@ public class PreferenceHelper {
putString(PROVIDER_EIP_DEFINITION, provider.getEipServiceJsonString()).
putString(PROVIDER_PRIVATE_KEY, provider.getPrivateKey()).
putString(PROVIDER_VPN_CERTIFICATE, provider.getVpnCertificate()).
- commit();
+ putString(PROVIDER_MOTD, provider.getMotdJsonString()).
+ putStringSet(PROVIDER_MOTD_HASHES, provider.getMotdLastSeenHashes()).
+ putLong(PROVIDER_MOTD_LAST_SEEN, provider.getLastMotdSeen()).
+ putLong(PROVIDER_MOTD_LAST_UPDATED, provider.getLastMotdUpdate());
+ if (apply) {
+ editor.apply();
+ } else {
+ editor.commit();
+ }
String providerDomain = provider.getDomain();
preferences.edit().putBoolean(PROVIDER_CONFIGURED, true).
@@ -99,9 +135,14 @@ public class PreferenceHelper {
putString(Provider.PROVIDER_API_IP + "." + providerDomain, provider.getProviderApiIp()).
putString(Provider.MAIN_URL + "." + providerDomain, provider.getMainUrlString()).
putString(Provider.GEOIP_URL + "." + providerDomain, provider.getGeoipUrl().toString()).
+ putString(Provider.MOTD_URL + "." + providerDomain, provider.getMotdUrl().toString()).
putString(Provider.KEY + "." + providerDomain, provider.getDefinitionString()).
putString(Provider.CA_CERT + "." + providerDomain, provider.getCaCert()).
putString(PROVIDER_EIP_DEFINITION + "." + providerDomain, provider.getEipServiceJsonString()).
+ putString(PROVIDER_MOTD + "." + providerDomain, provider.getMotdJsonString()).
+ putStringSet(PROVIDER_MOTD_HASHES + "." + providerDomain, provider.getMotdLastSeenHashes()).
+ putLong(PROVIDER_MOTD_LAST_SEEN + "." + providerDomain, provider.getLastMotdSeen()).
+ putLong(PROVIDER_MOTD_LAST_UPDATED + "." + providerDomain, provider.getLastMotdUpdate()).
apply();
}
@@ -132,9 +173,14 @@ public class PreferenceHelper {
remove(Provider.PROVIDER_API_IP + "." + providerDomain).
remove(Provider.MAIN_URL + "." + providerDomain).
remove(Provider.GEOIP_URL + "." + providerDomain).
+ remove(Provider.MOTD_URL + "." + providerDomain).
remove(PROVIDER_EIP_DEFINITION + "." + providerDomain).
remove(PROVIDER_PRIVATE_KEY + "." + providerDomain).
remove(PROVIDER_VPN_CERTIFICATE + "." + providerDomain).
+ remove(PROVIDER_MOTD + "." + providerDomain).
+ remove(PROVIDER_MOTD_HASHES + "." + providerDomain).
+ remove(PROVIDER_MOTD_LAST_SEEN + "." + providerDomain).
+ remove(PROVIDER_MOTD_LAST_UPDATED + "." + providerDomain).
apply();
}
@@ -146,9 +192,14 @@ public class PreferenceHelper {
remove(Provider.PROVIDER_API_IP).
remove(Provider.MAIN_URL).
remove(Provider.GEOIP_URL).
+ remove(Provider.MOTD_URL).
remove(PROVIDER_EIP_DEFINITION).
remove(PROVIDER_PRIVATE_KEY).
remove(PROVIDER_VPN_CERTIFICATE).
+ remove(PROVIDER_MOTD).
+ remove(PROVIDER_MOTD_HASHES).
+ remove(PROVIDER_MOTD_LAST_SEEN).
+ remove(PROVIDER_MOTD_LAST_UPDATED).
apply();
}
@@ -360,10 +411,7 @@ public class PreferenceHelper {
}
public static void setExcludedApps(Context context, Set<String> apps) {
- SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
- SharedPreferences.Editor prefsedit = prefs.edit();
- prefsedit.putStringSet(EXCLUDED_APPS, apps);
- prefsedit.apply();
+ putStringSet(context, EXCLUDED_APPS, apps);
}
public static Set<String> getExcludedApps(Context context) {
@@ -415,6 +463,14 @@ public class PreferenceHelper {
preferences.edit().putString(key, value).apply();
}
+ public static void putStringSet(Context context, String key, Set<String> value) {
+ if (context == null) {
+ return;
+ }
+ SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+ preferences.edit().putStringSet(key, value).apply();
+ }
+
public static boolean getBoolean(Context context, String key, Boolean defValue) {
if (context == null) {
return false;