summaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
authorcyberta <cyberta@riseup.net>2022-11-22 19:15:06 +0000
committercyberta <cyberta@riseup.net>2022-11-22 19:15:06 +0000
commitc3613c8521edb8f3e979331d2396977808581eeb (patch)
tree15aeeb09e1948466ae11b3591c7be89bb6de7bf9 /app/src/main/java
parent5bec8a2ab69cd3d457756ca43fa28b880f95befa (diff)
parent2d20c8e269a502945e981662abe1f7818090a618 (diff)
Merge branch 'motd' into 'master'
Motd Closes #9082 See merge request leap/bitmask_android!213
Diffstat (limited to 'app/src/main/java')
-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
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java7
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/motd/MotdClient.java47
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java1
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java48
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CustomProviderSetupActivity.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderListBaseActivity.java1
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java1
16 files changed, 548 insertions, 84 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;
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java
index 05991390..85cdb06c 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java
@@ -40,6 +40,7 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOAD
import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_GEOIP_JSON;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.DELAY;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_MOTD;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE;
@@ -385,6 +386,12 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta
parameters.putLong(DELAY, 500);
ProviderAPICommand.execute(appContext, QUIETLY_UPDATE_VPN_CERTIFICATE, parameters, provider);
}
+
+ if (provider.shouldUpdateMotdJson()) {
+ Bundle parameters = new Bundle();
+ parameters.putLong(DELAY, 500);
+ ProviderAPICommand.execute(appContext, DOWNLOAD_MOTD, parameters, provider);
+ }
finishGatewaySetup(false);
} else if ("TCP_CONNECT".equals(state)) {
changingGateway.set(false);
diff --git a/app/src/main/java/se/leap/bitmaskclient/motd/MotdClient.java b/app/src/main/java/se/leap/bitmaskclient/motd/MotdClient.java
new file mode 100644
index 00000000..d80a8992
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/motd/MotdClient.java
@@ -0,0 +1,47 @@
+package se.leap.bitmaskclient.motd;
+
+import androidx.annotation.WorkerThread;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import de.blinkt.openvpn.core.VpnStatus;
+import motd.IMessages;
+import motd.IMotd;
+import motd.Motd;
+import se.leap.bitmaskclient.base.models.Provider;
+
+public class MotdClient {
+ IMotd motd;
+
+ public MotdClient(Provider provider) {
+ motd = Motd.newMotd(provider.getMotdUrl().toString(), provider.getName(), "android");
+ }
+
+ @WorkerThread
+ public IMessages fetch() {
+ if (!VpnStatus.isVPNActive()) {
+ VpnStatus.logError("Tried to fetch Message of the Day while VPN was off.");
+ return null;
+ }
+
+ return Motd.newMessages(motd.fetchLatestAsJson());
+ }
+
+ @WorkerThread
+ public JSONObject fetchJson() {
+ if (!VpnStatus.isVPNActive()) {
+ VpnStatus.logError("Tried to fetch Message of the Day while VPN was off.");
+ return null;
+ }
+
+ try {
+ return new JSONObject(motd.fetchLatestAsJson());
+ } catch (NullPointerException | JSONException e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
index e45add50..86ce577b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
@@ -60,6 +60,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB
SET_UP_PROVIDER = "setUpProvider",
UPDATE_PROVIDER_DETAILS = "updateProviderDetails",
DOWNLOAD_GEOIP_JSON = "downloadGeoIpJson",
+ DOWNLOAD_MOTD = "downloadMotd",
SIGN_UP = "srpRegister",
LOG_IN = "srpAuth",
LOG_OUT = "logOut",
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
index 7b6a3ad6..14308875 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
@@ -35,10 +35,15 @@ import static se.leap.bitmaskclient.base.models.Constants.CREDENTIALS_PASSWORD;
import static se.leap.bitmaskclient.base.models.Constants.CREDENTIALS_USERNAME;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
+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.Provider.CA_CERT;
import static se.leap.bitmaskclient.base.models.Provider.GEOIP_URL;
+import static se.leap.bitmaskclient.base.models.Provider.MOTD_URL;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_API_IP;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_IP;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.getDomainFromMainURL;
@@ -47,6 +52,8 @@ import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormatted
import static se.leap.bitmaskclient.base.utils.ConfigHelper.parseRsaKeyFromString;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.deleteProviderDetailsFromPreferences;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getFromPersistedProvider;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getLongFromPersistedProvider;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getStringSetFromPersistedProvider;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.BACKEND_ERROR_KEY;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.BACKEND_ERROR_MESSAGE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE;
@@ -55,6 +62,7 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOAD
import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.DELAY;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_GEOIP_JSON;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_MOTD;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_SERVICE_JSON;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORID;
@@ -124,6 +132,7 @@ import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.Set;
import java.util.concurrent.TimeoutException;
import javax.net.ssl.SSLHandshakeException;
@@ -138,6 +147,7 @@ import se.leap.bitmaskclient.base.models.ProviderObservable;
import se.leap.bitmaskclient.base.utils.ConfigHelper;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.eip.EipStatus;
+import se.leap.bitmaskclient.motd.MotdClient;
import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
import se.leap.bitmaskclient.providersetup.models.LeapSRPSession;
import se.leap.bitmaskclient.providersetup.models.SrpCredentials;
@@ -299,6 +309,17 @@ public abstract class ProviderApiManagerBase {
}
ProviderObservable.getInstance().setProviderForDns(null);
break;
+ case DOWNLOAD_MOTD:
+ MotdClient client = new MotdClient(provider);
+ JSONObject motd = client.fetchJson();
+ if (motd != null) {
+ provider.setMotdJson(motd);
+ provider.setLastMotdUpdate(System.currentTimeMillis());
+ }
+ PreferenceHelper.storeProviderInPreferences(preferences, provider);
+ ProviderObservable.getInstance().updateProvider(provider);
+ break;
+
case UPDATE_INVALID_VPN_CERTIFICATE:
ProviderObservable.getInstance().setProviderForDns(provider);
result = updateVpnCertificate(provider);
@@ -914,7 +935,11 @@ public abstract class ProviderApiManagerBase {
provider.setVpnCertificate(getPersistedVPNCertificate(providerDomain));
provider.setProviderApiIp(getPersistedProviderApiIp(providerDomain));
provider.setProviderIp(getPersistedProviderIp(providerDomain));
- provider.setGeoipUrl(getPersistedGeoIp(providerDomain));
+ provider.setGeoipUrl(getPersistedGeoIp(providerDomain)); // TODO: do we really need to persist the Geoip URL??
+ provider.setLastMotdSeen(getPersistedMotdLastSeen(providerDomain));
+ provider.setMotdLastSeenHashes(getPersistedMotdHashes(providerDomain));
+ provider.setLastMotdUpdate(getPersistedMotdLastUpdate(providerDomain));
+ provider.setMotdJson(getPersistedMotd(providerDomain));
}
}
@@ -1045,6 +1070,27 @@ public abstract class ProviderApiManagerBase {
return getFromPersistedProvider(GEOIP_URL, providerDomain, preferences);
}
+ protected JSONObject getPersistedMotd(String providerDomain) {
+ try {
+ return new JSONObject(getFromPersistedProvider(PROVIDER_MOTD, providerDomain, preferences));
+ } catch (JSONException e) {
+ return new JSONObject();
+ }
+ }
+
+ protected long getPersistedMotdLastSeen(String providerDomain) {
+ return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_SEEN, providerDomain, preferences);
+ }
+
+ protected long getPersistedMotdLastUpdate(String providerDomain) {
+ return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_UPDATED, providerDomain, preferences);
+ }
+
+ protected Set<String> getPersistedMotdHashes(String providerDomain) {
+ return getStringSetFromPersistedProvider(PROVIDER_MOTD_HASHES, providerDomain, preferences);
+ }
+
+
protected boolean hasUpdatedProviderDetails(String domain) {
return preferences.contains(Provider.KEY + "." + domain) && preferences.contains(CA_CERT + "." + domain);
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
index 88413087..1ae2a033 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
@@ -24,6 +24,7 @@ import static se.leap.bitmaskclient.base.models.Constants.EXT_PEM;
import static se.leap.bitmaskclient.base.models.Constants.URLS;
import static se.leap.bitmaskclient.base.models.Provider.GEOIP_URL;
import static se.leap.bitmaskclient.base.models.Provider.MAIN_URL;
+import static se.leap.bitmaskclient.base.models.Provider.MOTD_URL;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_API_IP;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_IP;
import static se.leap.bitmaskclient.base.utils.FileHelper.createFile;
@@ -91,6 +92,7 @@ public class ProviderManager implements AdapteeCollection<Provider> {
String certificate = null;
String providerDefinition = null;
String geoipUrl = null;
+ String motdUrl = null;
try {
String provider = file.substring(0, file.length() - ".url".length());
InputStream providerFile = assetsManager.open(directory + "/" + file);
@@ -98,12 +100,13 @@ public class ProviderManager implements AdapteeCollection<Provider> {
providerIp = extractKeyFromInputStream(providerFile, PROVIDER_IP);
providerApiIp = extractKeyFromInputStream(providerFile, PROVIDER_API_IP);
geoipUrl = extractKeyFromInputStream(providerFile, GEOIP_URL);
+ motdUrl = extractKeyFromInputStream(providerFile, MOTD_URL);
certificate = loadInputStreamAsString(assetsManager.open(provider + EXT_PEM));
providerDefinition = loadInputStreamAsString(assetsManager.open(provider + EXT_JSON));
} catch (IOException e) {
e.printStackTrace();
}
- providers.add(new Provider(mainUrl, geoipUrl, providerIp, providerApiIp, certificate, providerDefinition));
+ providers.add(new Provider(mainUrl, geoipUrl, motdUrl, providerIp, providerApiIp, certificate, providerDefinition));
}
return providers;
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CustomProviderSetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CustomProviderSetupActivity.java
index 0fff1ee2..9cd46049 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CustomProviderSetupActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CustomProviderSetupActivity.java
@@ -36,6 +36,7 @@ import se.leap.bitmaskclient.providersetup.ProviderAPICommand;
import static se.leap.bitmaskclient.BuildConfig.customProviderApiIp;
import static se.leap.bitmaskclient.BuildConfig.customProviderIp;
+import static se.leap.bitmaskclient.BuildConfig.customProviderMotdUrl;
import static se.leap.bitmaskclient.BuildConfig.customProviderUrl;
import static se.leap.bitmaskclient.BuildConfig.geoipUrl;
import static se.leap.bitmaskclient.base.models.Constants.EXT_JSON;
@@ -72,7 +73,7 @@ public class CustomProviderSetupActivity extends ProviderSetupBaseActivity {
private void setDefaultProvider() {
try {
AssetManager assetsManager = getAssets();
- Provider customProvider = new Provider(customProviderUrl, geoipUrl, customProviderIp, customProviderApiIp);
+ Provider customProvider = new Provider(customProviderUrl, geoipUrl, customProviderMotdUrl, customProviderIp, customProviderApiIp);
String domain = ConfigHelper.getDomainFromMainURL(customProviderUrl);
String certificate = loadInputStreamAsString(assetsManager.open(domain + EXT_PEM));
String providerDefinition = loadInputStreamAsString(assetsManager.open(domain + EXT_JSON));
@@ -81,7 +82,7 @@ public class CustomProviderSetupActivity extends ProviderSetupBaseActivity {
setProvider(customProvider);
} catch (IOException | JSONException e) {
e.printStackTrace();
- setProvider(new Provider(customProviderUrl, geoipUrl, customProviderIp, customProviderApiIp));
+ setProvider(new Provider(customProviderUrl, geoipUrl, customProviderMotdUrl, customProviderIp, customProviderApiIp));
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderListBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderListBaseActivity.java
index 002335db..90ebfb4d 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderListBaseActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderListBaseActivity.java
@@ -95,6 +95,7 @@ public abstract class ProviderListBaseActivity extends ProviderSetupBaseActivity
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_CONFIGURE_LEAP) {
if (resultCode == RESULT_OK) {
setResult(resultCode, data);
diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java
index 59c1290a..5f7fa74a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java
+++ b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java
@@ -19,7 +19,6 @@ import android.content.Context;
import android.os.FileObserver;
import android.util.Log;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.torproject.jni.ClientTransportPluginInterface;