Implement IP2Country
							parent
							
								
									0a9f1f0e23
								
							
						
					
					
						commit
						51f0374109
					
				| @ -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<String, String>() | ||||
| 
 | ||||
|     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 | ||||
| } | ||||
					Loading…
					
					
				
		Reference in New Issue