diff options
author | cyberta <cyberta@riseup.net> | 2022-11-22 19:15:06 +0000 |
---|---|---|
committer | cyberta <cyberta@riseup.net> | 2022-11-22 19:15:06 +0000 |
commit | c3613c8521edb8f3e979331d2396977808581eeb (patch) | |
tree | 15aeeb09e1948466ae11b3591c7be89bb6de7bf9 /app/src/main/java | |
parent | 5bec8a2ab69cd3d457756ca43fa28b880f95befa (diff) | |
parent | 2d20c8e269a502945e981662abe1f7818090a618 (diff) |
Merge branch 'motd' into 'master'
Motd
Closes #9082
See merge request leap/bitmask_android!213
Diffstat (limited to 'app/src/main/java')
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; |