iPAVGeoGrabber.java

package net.nulll.uso.iPAVGeoGrabber;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import net.nulll.uso.iPAddressViewer.LogItem;
import net.nulll.uso.iPAddressViewer.Msg;
import net.nulll.uso.iPAddressViewer.iPAddressViewer;
import net.nulll.uso.iPAddressViewer.Uso;

import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;

public final class iPAVGeoGrabber extends JavaPlugin{
   
   static HashMap<String, GeoData> geoDataCache = new HashMap<String, GeoData>();
   private final JavaPlugin plugin = this;
   static List<DataSource> dataSources = new ArrayList<DataSource>();
   static YamlConfiguration geoStats = new YamlConfiguration();
   static HashMap<String,String> countryCodeMap = new HashMap<String,String>();
   
   @Override
   public void onEnable(){
      //Load file that contains past data source statistics
      geoStats = Uso.loadYaml(iPAddressViewer.staticPlugin.getDataFolder() + "/geoSourceStats.ipav");
      
      //Create a list of supported data sources, along with their limits
      dataSources = new ArrayList<DataSource>();
      dataSources.add(new DataSource("ipTracker", "http://www.ip-tracker.org/locator/ip-lookup.php?ip=x.x.x.x", -1, 24*60*60*1000));
      dataSources.add(new DataSource("whatismyipaddress", "http://whatismyipaddress.com/ip/x.x.x.x", 1, 5*60*1000));
      dataSources.add(new DataSource("ipinfo", "http://ipinfo.io/x.x.x.x/geo", 1000, 24*60*60*1000));
      dataSources.add(new DataSource("hackertarget", "http://api.hackertarget.com/geoip/?q=x.x.x.x", 100, 24*60*60*24));
      
      //Populate a map of country codes in order to translate them later if needed
      countryCodeMap.put("BD", "Bangladesh");
      countryCodeMap.put("BE", "Belgium");
      countryCodeMap.put("BF", "Burkina Faso");
      countryCodeMap.put("BG", "Bulgaria");
      countryCodeMap.put("BA", "Bosnia and Herzegovina");
      countryCodeMap.put("BB", "Barbados");
      countryCodeMap.put("WF", "Wallis and Futuna");
      countryCodeMap.put("BL", "Saint Barthelemy");
      countryCodeMap.put("BM", "Bermuda");
      countryCodeMap.put("BN", "Brunei");
      countryCodeMap.put("BO", "Bolivia");
      countryCodeMap.put("BH", "Bahrain");
      countryCodeMap.put("BI", "Burundi");
      countryCodeMap.put("BJ", "Benin");
      countryCodeMap.put("BT", "Bhutan");
      countryCodeMap.put("JM", "Jamaica");
      countryCodeMap.put("BV", "Bouvet Island");
      countryCodeMap.put("BW", "Botswana");
      countryCodeMap.put("WS", "Samoa");
      countryCodeMap.put("BQ", "Bonaire, Saint Eustatius and Saba ");
      countryCodeMap.put("BR", "Brazil");
      countryCodeMap.put("BS", "Bahamas");
      countryCodeMap.put("JE", "Jersey");
      countryCodeMap.put("BY", "Belarus");
      countryCodeMap.put("BZ", "Belize");
      countryCodeMap.put("RU", "Russia");
      countryCodeMap.put("RW", "Rwanda");
      countryCodeMap.put("RS", "Serbia");
      countryCodeMap.put("TL", "East Timor");
      countryCodeMap.put("RE", "Reunion");
      countryCodeMap.put("TM", "Turkmenistan");
      countryCodeMap.put("TJ", "Tajikistan");
      countryCodeMap.put("RO", "Romania");
      countryCodeMap.put("TK", "Tokelau");
      countryCodeMap.put("GW", "Guinea-Bissau");
      countryCodeMap.put("GU", "Guam");
      countryCodeMap.put("GT", "Guatemala");
      countryCodeMap.put("GS", "South Georgia and the South Sandwich Islands");
      countryCodeMap.put("GR", "Greece");
      countryCodeMap.put("GQ", "Equatorial Guinea");
      countryCodeMap.put("GP", "Guadeloupe");
      countryCodeMap.put("JP", "Japan");
      countryCodeMap.put("GY", "Guyana");
      countryCodeMap.put("GG", "Guernsey");
      countryCodeMap.put("GF", "French Guiana");
      countryCodeMap.put("GE", "Georgia");
      countryCodeMap.put("GD", "Grenada");
      countryCodeMap.put("GB", "United Kingdom");
      countryCodeMap.put("GA", "Gabon");
      countryCodeMap.put("SV", "El Salvador");
      countryCodeMap.put("GN", "Guinea");
      countryCodeMap.put("GM", "Gambia");
      countryCodeMap.put("GL", "Greenland");
      countryCodeMap.put("GI", "Gibraltar");
      countryCodeMap.put("GH", "Ghana");
      countryCodeMap.put("OM", "Oman");
      countryCodeMap.put("TN", "Tunisia");
      countryCodeMap.put("JO", "Jordan");
      countryCodeMap.put("HR", "Croatia");
      countryCodeMap.put("HT", "Haiti");
      countryCodeMap.put("HU", "Hungary");
      countryCodeMap.put("HK", "Hong Kong");
      countryCodeMap.put("HN", "Honduras");
      countryCodeMap.put("HM", "Heard Island and McDonald Islands");
      countryCodeMap.put("VE", "Venezuela");
      countryCodeMap.put("PR", "Puerto Rico");
      countryCodeMap.put("PS", "Palestinian Territory");
      countryCodeMap.put("PW", "Palau");
      countryCodeMap.put("PT", "Portugal");
      countryCodeMap.put("SJ", "Svalbard and Jan Mayen");
      countryCodeMap.put("PY", "Paraguay");
      countryCodeMap.put("IQ", "Iraq");
      countryCodeMap.put("PA", "Panama");
      countryCodeMap.put("PF", "French Polynesia");
      countryCodeMap.put("PG", "Papua New Guinea");
      countryCodeMap.put("PE", "Peru");
      countryCodeMap.put("PK", "Pakistan");
      countryCodeMap.put("PH", "Philippines");
      countryCodeMap.put("PN", "Pitcairn");
      countryCodeMap.put("PL", "Poland");
      countryCodeMap.put("PM", "Saint Pierre and Miquelon");
      countryCodeMap.put("ZM", "Zambia");
      countryCodeMap.put("EH", "Western Sahara");
      countryCodeMap.put("EE", "Estonia");
      countryCodeMap.put("EG", "Egypt");
      countryCodeMap.put("ZA", "South Africa");
      countryCodeMap.put("EC", "Ecuador");
      countryCodeMap.put("IT", "Italy");
      countryCodeMap.put("VN", "Vietnam");
      countryCodeMap.put("SB", "Solomon Islands");
      countryCodeMap.put("ET", "Ethiopia");
      countryCodeMap.put("SO", "Somalia");
      countryCodeMap.put("ZW", "Zimbabwe");
      countryCodeMap.put("SA", "Saudi Arabia");
      countryCodeMap.put("ES", "Spain");
      countryCodeMap.put("ER", "Eritrea");
      countryCodeMap.put("ME", "Montenegro");
      countryCodeMap.put("MD", "Moldova");
      countryCodeMap.put("MG", "Madagascar");
      countryCodeMap.put("MF", "Saint Martin");
      countryCodeMap.put("MA", "Morocco");
      countryCodeMap.put("MC", "Monaco");
      countryCodeMap.put("UZ", "Uzbekistan");
      countryCodeMap.put("MM", "Myanmar");
      countryCodeMap.put("ML", "Mali");
      countryCodeMap.put("MO", "Macao");
      countryCodeMap.put("MN", "Mongolia");
      countryCodeMap.put("MH", "Marshall Islands");
      countryCodeMap.put("MK", "Macedonia");
      countryCodeMap.put("MU", "Mauritius");
      countryCodeMap.put("MT", "Malta");
      countryCodeMap.put("MW", "Malawi");
      countryCodeMap.put("MV", "Maldives");
      countryCodeMap.put("MQ", "Martinique");
      countryCodeMap.put("MP", "Northern Mariana Islands");
      countryCodeMap.put("MS", "Montserrat");
      countryCodeMap.put("MR", "Mauritania");
      countryCodeMap.put("IM", "Isle of Man");
      countryCodeMap.put("UG", "Uganda");
      countryCodeMap.put("TZ", "Tanzania");
      countryCodeMap.put("MY", "Malaysia");
      countryCodeMap.put("MX", "Mexico");
      countryCodeMap.put("IL", "Israel");
      countryCodeMap.put("FR", "France");
      countryCodeMap.put("IO", "British Indian Ocean Territory");
      countryCodeMap.put("SH", "Saint Helena");
      countryCodeMap.put("FI", "Finland");
      countryCodeMap.put("FJ", "Fiji");
      countryCodeMap.put("FK", "Falkland Islands");
      countryCodeMap.put("FM", "Micronesia");
      countryCodeMap.put("FO", "Faroe Islands");
      countryCodeMap.put("NI", "Nicaragua");
      countryCodeMap.put("NL", "Netherlands");
      countryCodeMap.put("NO", "Norway");
      countryCodeMap.put("NA", "Namibia");
      countryCodeMap.put("VU", "Vanuatu");
      countryCodeMap.put("NC", "New Caledonia");
      countryCodeMap.put("NE", "Niger");
      countryCodeMap.put("NF", "Norfolk Island");
      countryCodeMap.put("NG", "Nigeria");
      countryCodeMap.put("NZ", "New Zealand");
      countryCodeMap.put("NP", "Nepal");
      countryCodeMap.put("NR", "Nauru");
      countryCodeMap.put("NU", "Niue");
      countryCodeMap.put("CK", "Cook Islands");
      countryCodeMap.put("XK", "Kosovo");
      countryCodeMap.put("CI", "Ivory Coast");
      countryCodeMap.put("CH", "Switzerland");
      countryCodeMap.put("CO", "Colombia");
      countryCodeMap.put("CN", "China");
      countryCodeMap.put("CM", "Cameroon");
      countryCodeMap.put("CL", "Chile");
      countryCodeMap.put("CC", "Cocos Islands");
      countryCodeMap.put("CA", "Canada");
      countryCodeMap.put("CAN", "Canada");
      countryCodeMap.put("CG", "Republic of the Congo");
      countryCodeMap.put("CF", "Central African Republic");
      countryCodeMap.put("CD", "Democratic Republic of the Congo");
      countryCodeMap.put("CZ", "Czech Republic");
      countryCodeMap.put("CY", "Cyprus");
      countryCodeMap.put("CX", "Christmas Island");
      countryCodeMap.put("CR", "Costa Rica");
      countryCodeMap.put("CW", "Curacao");
      countryCodeMap.put("CV", "Cape Verde");
      countryCodeMap.put("CU", "Cuba");
      countryCodeMap.put("SZ", "Swaziland");
      countryCodeMap.put("SY", "Syria");
      countryCodeMap.put("SX", "Sint Maarten");
      countryCodeMap.put("KG", "Kyrgyzstan");
      countryCodeMap.put("KE", "Kenya");
      countryCodeMap.put("SS", "South Sudan");
      countryCodeMap.put("SR", "Suriname");
      countryCodeMap.put("KI", "Kiribati");
      countryCodeMap.put("KH", "Cambodia");
      countryCodeMap.put("KN", "Saint Kitts and Nevis");
      countryCodeMap.put("KM", "Comoros");
      countryCodeMap.put("ST", "Sao Tome and Principe");
      countryCodeMap.put("SK", "Slovakia");
      countryCodeMap.put("KR", "South Korea");
      countryCodeMap.put("SI", "Slovenia");
      countryCodeMap.put("KP", "North Korea");
      countryCodeMap.put("KW", "Kuwait");
      countryCodeMap.put("SN", "Senegal");
      countryCodeMap.put("SM", "San Marino");
      countryCodeMap.put("SL", "Sierra Leone");
      countryCodeMap.put("SC", "Seychelles");
      countryCodeMap.put("KZ", "Kazakhstan");
      countryCodeMap.put("KY", "Cayman Islands");
      countryCodeMap.put("SG", "Singapore");
      countryCodeMap.put("SE", "Sweden");
      countryCodeMap.put("SD", "Sudan");
      countryCodeMap.put("DO", "Dominican Republic");
      countryCodeMap.put("DM", "Dominica");
      countryCodeMap.put("DJ", "Djibouti");
      countryCodeMap.put("DK", "Denmark");
      countryCodeMap.put("VG", "British Virgin Islands");
      countryCodeMap.put("DE", "Germany");
      countryCodeMap.put("YE", "Yemen");
      countryCodeMap.put("DZ", "Algeria");
      countryCodeMap.put("US", "United States");
      countryCodeMap.put("USA", "United States");
      countryCodeMap.put("UY", "Uruguay");
      countryCodeMap.put("YT", "Mayotte");
      countryCodeMap.put("UM", "United States Minor Outlying Islands");
      countryCodeMap.put("LB", "Lebanon");
      countryCodeMap.put("LC", "Saint Lucia");
      countryCodeMap.put("LA", "Laos");
      countryCodeMap.put("TV", "Tuvalu");
      countryCodeMap.put("TW", "Taiwan");
      countryCodeMap.put("TT", "Trinidad and Tobago");
      countryCodeMap.put("TR", "Turkey");
      countryCodeMap.put("LK", "Sri Lanka");
      countryCodeMap.put("LI", "Liechtenstein");
      countryCodeMap.put("LV", "Latvia");
      countryCodeMap.put("TO", "Tonga");
      countryCodeMap.put("LT", "Lithuania");
      countryCodeMap.put("LU", "Luxembourg");
      countryCodeMap.put("LR", "Liberia");
      countryCodeMap.put("LS", "Lesotho");
      countryCodeMap.put("TH", "Thailand");
      countryCodeMap.put("TF", "French Southern Territories");
      countryCodeMap.put("TG", "Togo");
      countryCodeMap.put("TD", "Chad");
      countryCodeMap.put("TC", "Turks and Caicos Islands");
      countryCodeMap.put("LY", "Libya");
      countryCodeMap.put("VA", "Vatican");
      countryCodeMap.put("VC", "Saint Vincent and the Grenadines");
      countryCodeMap.put("AE", "United Arab Emirates");
      countryCodeMap.put("AD", "Andorra");
      countryCodeMap.put("AG", "Antigua and Barbuda");
      countryCodeMap.put("AF", "Afghanistan");
      countryCodeMap.put("AI", "Anguilla");
      countryCodeMap.put("VI", "U.S. Virgin Islands");
      countryCodeMap.put("IS", "Iceland");
      countryCodeMap.put("IR", "Iran");
      countryCodeMap.put("AM", "Armenia");
      countryCodeMap.put("AL", "Albania");
      countryCodeMap.put("AO", "Angola");
      countryCodeMap.put("AQ", "Antarctica");
      countryCodeMap.put("AS", "American Samoa");
      countryCodeMap.put("AR", "Argentina");
      countryCodeMap.put("AU", "Australia");
      countryCodeMap.put("AT", "Austria");
      countryCodeMap.put("AW", "Aruba");
      countryCodeMap.put("IN", "India");
      countryCodeMap.put("AX", "Aland Islands");
      countryCodeMap.put("AZ", "Azerbaijan");
      countryCodeMap.put("IE", "Ireland");
      countryCodeMap.put("ID", "Indonesia");
      countryCodeMap.put("UA", "Ukraine");
      countryCodeMap.put("QA", "Qatar");
      countryCodeMap.put("MZ", "Mozambique");
   }
   
   @Override
   public void onDisable() {
       Bukkit.getScheduler().cancelTasks(plugin);
   }
   
   public static void queryForGeoData(LogItem logItem){
      
      long decimalAddress = addressToLong(logItem.getAddress());
      
      //Test to see if the IP address is reserved and not valid for lookup.
      if(logItem.getAddress().startsWith("0.") ||
         logItem.getAddress().startsWith("10.") ||
         logItem.getAddress().startsWith("127.") ||
         logItem.getAddress().startsWith("169.254.") ||
         logItem.getAddress().startsWith("192.0.0.") ||
         logItem.getAddress().startsWith("192.0.2.") ||
         logItem.getAddress().startsWith("192.88.99.") ||
         logItem.getAddress().startsWith("192.168.") ||
         logItem.getAddress().startsWith("198.51.100.") ||
         logItem.getAddress().startsWith("203.0.113.") ||
         (decimalAddress >= addressToLong("100.64.0.0") && decimalAddress <= addressToLong("100.127.255.255")) ||
         (decimalAddress >= addressToLong("172.16.0.0") && decimalAddress <= addressToLong("172.31.255.255")) ||
         (decimalAddress >= addressToLong("198.18.0.0") && decimalAddress <= addressToLong("198.19.255.255")) ||
         (decimalAddress >= addressToLong("224.0.0.0") && decimalAddress <= addressToLong("239.255.255.255")) ||
         (decimalAddress >= addressToLong("240.0.0.0") && decimalAddress <= addressToLong("255.255.255.255"))
      ){
         Msg.msgC(logItem.getAddress() + " is a private address and can't be geographically located.");
         
         //Tell iPAV to display the user connection info (even though a geo location wasn't found to add to it)
         iPAddressViewer.forwardGeoData(logItem);
         return;
      }
      
      //Check to see if the address was already looked up recently (after the last /reload or server startup)
      GeoData geoData = geoDataCache.get(logItem.getAddress());

      //If it hasn't been successfully queried yet, look it up. Else, use the previous location data
      if(geoData==null){
         geoData = new GeoData(logItem, logItem.getAddress());
         //Add the newly acquired location data to the cache if it was successful
         if(geoData.geoDataFound()){
            geoDataCache.put(logItem.getAddress(), geoData);
         }
      }else{
         //Give the iPAV log the location data from the cache
         logItem.applyGeoData(geoData.city, geoData.state, geoData.country);
         //Tell iPAV to display the user connection info
         iPAddressViewer.forwardGeoData(logItem);
      }
   }
   
   //Convert a standard IPv4 address to a long integer to simplify address-range testing
   static long addressToLong(String address){
      int[] array = new int[4];
      array[0] = Integer.valueOf(address.split("\\.")[0]).intValue();
      array[1] = Integer.valueOf(address.split("\\.")[1]).intValue();
      array[2] = Integer.valueOf(address.split("\\.")[2]).intValue();
      array[3] = Integer.valueOf(address.split("\\.")[3]).intValue();
      return array[0]*256*256*256 + array[1]*256*256 + array[2]*256 + array[3];
   }
}