From 51f037410904481ae55b216f73379ef37abbc7c3 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Tue, 2 Jun 2020 15:00:37 +1000 Subject: [PATCH 1/5] Implement IP2Country --- build.gradle | 2 + .../securesms/loki/utilities/IP2Country.kt | 116 ++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt diff --git a/build.gradle b/build.gradle index ad5c0ac04d..accac8f19d 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,7 @@ buildscript { ext.rss_parser_version = "2.0.4" ext.google_services_version = "4.3.3" ext.firebase_messaging_version = "18.0.0" + ext.opencsv_version = "4.6" repositories { mavenLocal() @@ -188,6 +189,7 @@ dependencies { implementation "com.jakewharton.rxbinding3:rxbinding:3.1.0" implementation "com.github.tbruyelle:rxpermissions:0.10.2" implementation "com.github.ybq:Android-SpinKit:1.4.0" + implementation "com.opencsv:opencsv:$opencsv_version" } def canonicalVersionCode = 52 diff --git a/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt b/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt new file mode 100644 index 0000000000..2181e70b45 --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt @@ -0,0 +1,116 @@ +package org.thoughtcrime.securesms.loki.utilities + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.support.v4.content.LocalBroadcastManager +import android.util.Log +import com.opencsv.CSVReader +import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI +import java.io.File +import java.io.FileOutputStream +import java.io.FileReader + +class IP2Country private constructor(private val context: Context) { + private val pathsBuiltEventReceiver: BroadcastReceiver + private val countryNamesCache = mutableMapOf() + + private val ipv4Table by lazy { + loadFile("geolite2_country_blocks_ipv4.csv") + } + + private val countryNamesTable by lazy { + loadFile("geolite2_country_locations_english.csv") + } + + // region Initialization + companion object { + + public lateinit var shared: IP2Country + + public fun configureIfNeeded(context: Context) { + if (::shared.isInitialized) { return; } + shared = IP2Country(context) + } + } + + init { + preloadCountriesIfNeeded() + pathsBuiltEventReceiver = object : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + preloadCountriesIfNeeded() + } + } + LocalBroadcastManager.getInstance(context).registerReceiver(pathsBuiltEventReceiver, IntentFilter("pathsBuilt")) + } + + // TODO: Deinit? + // endregion + + // region Implementation + private fun loadFile(fileName: String): File { + val directory = File(context.applicationInfo.dataDir) + val file = File(directory, fileName) + if (directory.list().contains(fileName)) { return file } + val inputStream = context.assets.open("csv/$fileName") + val outputStream = FileOutputStream(file) + val buffer = ByteArray(1024) + while (true) { + val count = inputStream.read(buffer) + if (count < 0) { break } + outputStream.write(buffer, 0, count) + } + inputStream.close() + outputStream.close() + return file + } + + private fun getCountry(ip: String): String { + var truncatedIP = ip + fun getCountryInternal(): String { + val country = countryNamesCache[ip] + if (country != null) { return country } + val ipv4TableReader = CSVReader(FileReader(ipv4Table.absoluteFile)) + val countryNamesTableReader = CSVReader(FileReader(ipv4Table.absoluteFile)) + var ipv4TableLine = ipv4TableReader.readNext() + while (ipv4TableLine != null) { + if (!ipv4TableLine[0].startsWith(truncatedIP)) { + ipv4TableLine = ipv4TableReader.readNext() + continue + } + val countryID = ipv4TableLine[1] + var countryNamesTableLine = countryNamesTableReader.readNext() + while (countryNamesTableLine != null) { + if (countryNamesTableLine[0] != countryID) { + countryNamesTableLine = countryNamesTableReader.readNext() + continue + } + @Suppress("NAME_SHADOWING") val country = countryNamesTableLine[5] + countryNamesCache[ip] = country + return country + } + } + if (truncatedIP.contains(".") && !truncatedIP.endsWith(".")) { // The fuzziest we want to go is xxx.x + truncatedIP.dropLast(1) + if (truncatedIP.endsWith(".")) { truncatedIP.dropLast(1) } + return getCountryInternal() + } else { + return "Unknown Country" + } + } + return getCountryInternal() + } + + private fun preloadCountriesIfNeeded() { + Thread { + val path = OnionRequestAPI.paths.firstOrNull() ?: return@Thread + path.forEach { snode -> + getCountry(snode.ip) // Preload if needed + } + Log.d("Loki", "Finished preloading onion request path countries.") + }.start() + } + // endregion +} \ No newline at end of file From 1217e5278fc606a2c06857f5c027c17d1bbb2474 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Tue, 2 Jun 2020 15:18:09 +1000 Subject: [PATCH 2/5] Show countries rather than IPs --- .../securesms/loki/activities/HomeActivity.kt | 6 ++---- .../securesms/loki/activities/PathActivity.kt | 9 +++------ .../thoughtcrime/securesms/loki/utilities/IP2Country.kt | 8 ++++---- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt index fdfc11a0b0..c45dbd5539 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt @@ -34,10 +34,7 @@ import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.loki.dialogs.PNModeBottomSheet import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation -import org.thoughtcrime.securesms.loki.utilities.getColorWithID -import org.thoughtcrime.securesms.loki.utilities.push -import org.thoughtcrime.securesms.loki.utilities.recipient -import org.thoughtcrime.securesms.loki.utilities.show +import org.thoughtcrime.securesms.loki.utilities.* import org.thoughtcrime.securesms.loki.views.ConversationView import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegate import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate @@ -70,6 +67,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { super.onCreate(savedInstanceState, isReady) + IP2Country.configureIfNeeded(this) // Process any outstanding deletes val threadDatabase = DatabaseFactory.getThreadDatabase(this) val archivedConversationCount = threadDatabase.archivedConversationListCount diff --git a/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt index e9791fd576..56d5c7f4a1 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt @@ -22,12 +22,9 @@ import kotlinx.android.synthetic.main.activity_path.* import network.loki.messenger.R import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.loki.utilities.animateSizeChange -import org.thoughtcrime.securesms.loki.utilities.fadeIn -import org.thoughtcrime.securesms.loki.utilities.fadeOut -import org.thoughtcrime.securesms.loki.utilities.getColorWithID -import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI +import org.thoughtcrime.securesms.loki.utilities.* import org.whispersystems.signalservice.loki.api.Snode +import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI class PathActivity : PassphraseRequiredActionBarActivity() { private val broadcastReceivers = mutableListOf() @@ -142,7 +139,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() { private fun getPathRow(snode: Snode, location: LineView.Location, dotAnimationStartDelay: Long, dotAnimationRepeatInterval: Long, isGuardSnode: Boolean): LinearLayout { val title = if (isGuardSnode) resources.getString(R.string.activity_path_guard_node_row_title) else resources.getString(R.string.activity_path_service_node_row_title) - val subtitle = snode.toString().removePrefix("https://").substringBefore(":") + val subtitle = IP2Country.shared.getCountry(snode.ip) return getPathRow(title, subtitle, location, dotAnimationStartDelay, dotAnimationRepeatInterval) } // endregion diff --git a/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt b/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt index 2181e70b45..c45977d7a2 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt @@ -67,13 +67,13 @@ class IP2Country private constructor(private val context: Context) { return file } - private fun getCountry(ip: String): String { + fun getCountry(ip: String): String { var truncatedIP = ip fun getCountryInternal(): String { val country = countryNamesCache[ip] if (country != null) { return country } val ipv4TableReader = CSVReader(FileReader(ipv4Table.absoluteFile)) - val countryNamesTableReader = CSVReader(FileReader(ipv4Table.absoluteFile)) + val countryNamesTableReader = CSVReader(FileReader(countryNamesTable.absoluteFile)) var ipv4TableLine = ipv4TableReader.readNext() while (ipv4TableLine != null) { if (!ipv4TableLine[0].startsWith(truncatedIP)) { @@ -93,8 +93,8 @@ class IP2Country private constructor(private val context: Context) { } } if (truncatedIP.contains(".") && !truncatedIP.endsWith(".")) { // The fuzziest we want to go is xxx.x - truncatedIP.dropLast(1) - if (truncatedIP.endsWith(".")) { truncatedIP.dropLast(1) } + truncatedIP = truncatedIP.dropLast(1) + if (truncatedIP.endsWith(".")) { truncatedIP = truncatedIP.dropLast(1) } return getCountryInternal() } else { return "Unknown Country" From 6cb67148933f9cc94a444bf333a34279dfd47a29 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Tue, 2 Jun 2020 15:22:32 +1000 Subject: [PATCH 3/5] Ditch rebuild path button --- res/layout/activity_path.xml | 4 +-- res/menu/menu_path.xml | 10 ------- res/values/strings.xml | 2 +- .../securesms/loki/activities/PathActivity.kt | 26 +------------------ 4 files changed, 4 insertions(+), 38 deletions(-) delete mode 100644 res/menu/menu_path.xml diff --git a/res/layout/activity_path.xml b/res/layout/activity_path.xml index 6dd8dff316..63d549c9b2 100644 --- a/res/layout/activity_path.xml +++ b/res/layout/activity_path.xml @@ -45,10 +45,10 @@