Geo-IPinfo/lib/Geo/IPinfoLite.pm
package Geo::IPinfoLite;
use 5.006;
use strict;
use warnings;
use Cache::LRU;
use LWP::UserAgent;
use HTTP::Headers;
use JSON;
use Geo::DetailsLite;
use Net::CIDR;
use Net::CIDR::Set;
our $VERSION = '3.2.0';
use constant DEFAULT_CACHE_MAX_SIZE => 4096;
use constant DEFAULT_CACHE_TTL => 86_400;
use constant DEFAULT_TIMEOUT => 2;
use constant HTTP_TOO_MANY_REQUEST => 429;
my %valid_fields = (
ip => 1,
asn => 1,
as_name => 1,
as_domain => 1,
country_code => 1,
country => 1,
continent_code => 1,
continent => 1,
);
my $base_url = 'https://api.ipinfo.io/lite/';
my $country_flag_url = 'https://cdn.ipinfo.io/static/images/countries-flags/';
my $cache_ttl = 0;
my $custom_cache = 0;
my %default_countries = (
"BD" => "Bangladesh",
"BE" => "Belgium",
"BF" => "Burkina Faso",
"BG" => "Bulgaria",
"BA" => "Bosnia and Herzegovina",
"BB" => "Barbados",
"WF" => "Wallis and Futuna",
"BL" => "Saint Barthelemy",
"BM" => "Bermuda",
"BN" => "Brunei",
"BO" => "Bolivia",
"BH" => "Bahrain",
"BI" => "Burundi",
"BJ" => "Benin",
"BT" => "Bhutan",
"JM" => "Jamaica",
"BV" => "Bouvet Island",
"BW" => "Botswana",
"WS" => "Samoa",
"BQ" => "Bonaire, Saint Eustatius and Saba ",
"BR" => "Brazil",
"BS" => "Bahamas",
"JE" => "Jersey",
"BY" => "Belarus",
"BZ" => "Belize",
"RU" => "Russia",
"RW" => "Rwanda",
"RS" => "Serbia",
"TL" => "East Timor",
"RE" => "Reunion",
"TM" => "Turkmenistan",
"TJ" => "Tajikistan",
"RO" => "Romania",
"TK" => "Tokelau",
"GW" => "Guinea-Bissau",
"GU" => "Guam",
"GT" => "Guatemala",
"GS" => "South Georgia and the South Sandwich Islands",
"GR" => "Greece",
"GQ" => "Equatorial Guinea",
"GP" => "Guadeloupe",
"JP" => "Japan",
"GY" => "Guyana",
"GG" => "Guernsey",
"GF" => "French Guiana",
"GE" => "Georgia",
"GD" => "Grenada",
"GB" => "United Kingdom",
"GA" => "Gabon",
"SV" => "El Salvador",
"GN" => "Guinea",
"GM" => "Gambia",
"GL" => "Greenland",
"GI" => "Gibraltar",
"GH" => "Ghana",
"OM" => "Oman",
"TN" => "Tunisia",
"JO" => "Jordan",
"HR" => "Croatia",
"HT" => "Haiti",
"HU" => "Hungary",
"HK" => "Hong Kong",
"HN" => "Honduras",
"HM" => "Heard Island and McDonald Islands",
"VE" => "Venezuela",
"PR" => "Puerto Rico",
"PS" => "Palestinian Territory",
"PW" => "Palau",
"PT" => "Portugal",
"SJ" => "Svalbard and Jan Mayen",
"PY" => "Paraguay",
"IQ" => "Iraq",
"PA" => "Panama",
"PF" => "French Polynesia",
"PG" => "Papua New Guinea",
"PE" => "Peru",
"PK" => "Pakistan",
"PH" => "Philippines",
"PN" => "Pitcairn",
"PL" => "Poland",
"PM" => "Saint Pierre and Miquelon",
"ZM" => "Zambia",
"EH" => "Western Sahara",
"EE" => "Estonia",
"EG" => "Egypt",
"ZA" => "South Africa",
"EC" => "Ecuador",
"IT" => "Italy",
"VN" => "Vietnam",
"SB" => "Solomon Islands",
"ET" => "Ethiopia",
"SO" => "Somalia",
"ZW" => "Zimbabwe",
"SA" => "Saudi Arabia",
"ES" => "Spain",
"ER" => "Eritrea",
"ME" => "Montenegro",
"MD" => "Moldova",
"MG" => "Madagascar",
"MF" => "Saint Martin",
"MA" => "Morocco",
"MC" => "Monaco",
"UZ" => "Uzbekistan",
"MM" => "Myanmar",
"ML" => "Mali",
"MO" => "Macao",
"MN" => "Mongolia",
"MH" => "Marshall Islands",
"MK" => "Macedonia",
"MU" => "Mauritius",
"MT" => "Malta",
"MW" => "Malawi",
"MV" => "Maldives",
"MQ" => "Martinique",
"MP" => "Northern Mariana Islands",
"MS" => "Montserrat",
"MR" => "Mauritania",
"IM" => "Isle of Man",
"UG" => "Uganda",
"TZ" => "Tanzania",
"MY" => "Malaysia",
"MX" => "Mexico",
"IL" => "Israel",
"FR" => "France",
"IO" => "British Indian Ocean Territory",
"SH" => "Saint Helena",
"FI" => "Finland",
"FJ" => "Fiji",
"FK" => "Falkland Islands",
"FM" => "Micronesia",
"FO" => "Faroe Islands",
"NI" => "Nicaragua",
"NL" => "Netherlands",
"NO" => "Norway",
"NA" => "Namibia",
"VU" => "Vanuatu",
"NC" => "New Caledonia",
"NE" => "Niger",
"NF" => "Norfolk Island",
"NG" => "Nigeria",
"NZ" => "New Zealand",
"NP" => "Nepal",
"NR" => "Nauru",
"NU" => "Niue",
"CK" => "Cook Islands",
"XK" => "Kosovo",
"CI" => "Ivory Coast",
"CH" => "Switzerland",
"CO" => "Colombia",
"CN" => "China",
"CM" => "Cameroon",
"CL" => "Chile",
"CC" => "Cocos Islands",
"CA" => "Canada",
"CG" => "Republic of the Congo",
"CF" => "Central African Republic",
"CD" => "Democratic Republic of the Congo",
"CZ" => "Czech Republic",
"CY" => "Cyprus",
"CX" => "Christmas Island",
"CR" => "Costa Rica",
"CW" => "Curacao",
"CV" => "Cape Verde",
"CU" => "Cuba",
"SZ" => "Swaziland",
"SY" => "Syria",
"SX" => "Sint Maarten",
"KG" => "Kyrgyzstan",
"KE" => "Kenya",
"SS" => "South Sudan",
"SR" => "Suriname",
"KI" => "Kiribati",
"KH" => "Cambodia",
"KN" => "Saint Kitts and Nevis",
"KM" => "Comoros",
"ST" => "Sao Tome and Principe",
"SK" => "Slovakia",
"KR" => "South Korea",
"SI" => "Slovenia",
"KP" => "North Korea",
"KW" => "Kuwait",
"SN" => "Senegal",
"SM" => "San Marino",
"SL" => "Sierra Leone",
"SC" => "Seychelles",
"KZ" => "Kazakhstan",
"KY" => "Cayman Islands",
"SG" => "Singapore",
"SE" => "Sweden",
"SD" => "Sudan",
"DO" => "Dominican Republic",
"DM" => "Dominica",
"DJ" => "Djibouti",
"DK" => "Denmark",
"VG" => "British Virgin Islands",
"DE" => "Germany",
"YE" => "Yemen",
"DZ" => "Algeria",
"US" => "United States",
"UY" => "Uruguay",
"YT" => "Mayotte",
"UM" => "United States Minor Outlying Islands",
"LB" => "Lebanon",
"LC" => "Saint Lucia",
"LA" => "Laos",
"TV" => "Tuvalu",
"TW" => "Taiwan",
"TT" => "Trinidad and Tobago",
"TR" => "Turkey",
"LK" => "Sri Lanka",
"LI" => "Liechtenstein",
"LV" => "Latvia",
"TO" => "Tonga",
"LT" => "Lithuania",
"LU" => "Luxembourg",
"LR" => "Liberia",
"LS" => "Lesotho",
"TH" => "Thailand",
"TF" => "French Southern Territories",
"TG" => "Togo",
"TD" => "Chad",
"TC" => "Turks and Caicos Islands",
"LY" => "Libya",
"VA" => "Vatican",
"VC" => "Saint Vincent and the Grenadines",
"AE" => "United Arab Emirates",
"AD" => "Andorra",
"AG" => "Antigua and Barbuda",
"AF" => "Afghanistan",
"AI" => "Anguilla",
"VI" => "U.S. Virgin Islands",
"IS" => "Iceland",
"IR" => "Iran",
"AM" => "Armenia",
"AL" => "Albania",
"AO" => "Angola",
"AQ" => "Antarctica",
"AS" => "American Samoa",
"AR" => "Argentina",
"AU" => "Australia",
"AT" => "Austria",
"AW" => "Aruba",
"IN" => "India",
"AX" => "Aland Islands",
"AZ" => "Azerbaijan",
"IE" => "Ireland",
"ID" => "Indonesia",
"UA" => "Ukraine",
"QA" => "Qatar",
"MZ" => "Mozambique"
);
my %default_countries_flags = (
'AD' => { 'emoji' => '๐ฆ๐ฉ', 'unicode' => 'U+1F1E6 U+1F1E9' },
'AE' => { 'emoji' => '๐ฆ๐ช', 'unicode' => 'U+1F1E6 U+1F1EA' },
'AF' => { 'emoji' => '๐ฆ๐ซ', 'unicode' => 'U+1F1E6 U+1F1EB' },
'AG' => { 'emoji' => '๐ฆ๐ฌ', 'unicode' => 'U+1F1E6 U+1F1EC' },
'AI' => { 'emoji' => '๐ฆ๐ฎ', 'unicode' => 'U+1F1E6 U+1F1EE' },
'AL' => { 'emoji' => '๐ฆ๐ฑ', 'unicode' => 'U+1F1E6 U+1F1F1' },
'AM' => { 'emoji' => '๐ฆ๐ฒ', 'unicode' => 'U+1F1E6 U+1F1F2' },
'AO' => { 'emoji' => '๐ฆ๐ด', 'unicode' => 'U+1F1E6 U+1F1F4' },
'AQ' => { 'emoji' => '๐ฆ๐ถ', 'unicode' => 'U+1F1E6 U+1F1F6' },
'AR' => { 'emoji' => '๐ฆ๐ท', 'unicode' => 'U+1F1E6 U+1F1F7' },
'AS' => { 'emoji' => '๐ฆ๐ธ', 'unicode' => 'U+1F1E6 U+1F1F8' },
'AT' => { 'emoji' => '๐ฆ๐น', 'unicode' => 'U+1F1E6 U+1F1F9' },
'AU' => { 'emoji' => '๐ฆ๐บ', 'unicode' => 'U+1F1E6 U+1F1FA' },
'AW' => { 'emoji' => '๐ฆ๐ผ', 'unicode' => 'U+1F1E6 U+1F1FC' },
'AX' => { 'emoji' => '๐ฆ๐ฝ', 'unicode' => 'U+1F1E6 U+1F1FD' },
'AZ' => { 'emoji' => '๐ฆ๐ฟ', 'unicode' => 'U+1F1E6 U+1F1FF' },
'BA' => { 'emoji' => '๐ง๐ฆ', 'unicode' => 'U+1F1E7 U+1F1E6' },
'BB' => { 'emoji' => '๐ง๐ง', 'unicode' => 'U+1F1E7 U+1F1E7' },
'BD' => { 'emoji' => '๐ง๐ฉ', 'unicode' => 'U+1F1E7 U+1F1E9' },
'BE' => { 'emoji' => '๐ง๐ช', 'unicode' => 'U+1F1E7 U+1F1EA' },
'BF' => { 'emoji' => '๐ง๐ซ', 'unicode' => 'U+1F1E7 U+1F1EB' },
'BG' => { 'emoji' => '๐ง๐ฌ', 'unicode' => 'U+1F1E7 U+1F1EC' },
'BH' => { 'emoji' => '๐ง๐ญ', 'unicode' => 'U+1F1E7 U+1F1ED' },
'BI' => { 'emoji' => '๐ง๐ฎ', 'unicode' => 'U+1F1E7 U+1F1EE' },
'BJ' => { 'emoji' => '๐ง๐ฏ', 'unicode' => 'U+1F1E7 U+1F1EF' },
'BL' => { 'emoji' => '๐ง๐ฑ', 'unicode' => 'U+1F1E7 U+1F1F1' },
'BM' => { 'emoji' => '๐ง๐ฒ', 'unicode' => 'U+1F1E7 U+1F1F2' },
'BN' => { 'emoji' => '๐ง๐ณ', 'unicode' => 'U+1F1E7 U+1F1F3' },
'BO' => { 'emoji' => '๐ง๐ด', 'unicode' => 'U+1F1E7 U+1F1F4' },
'BQ' => { 'emoji' => '๐ง๐ถ', 'unicode' => 'U+1F1E7 U+1F1F6' },
'BR' => { 'emoji' => '๐ง๐ท', 'unicode' => 'U+1F1E7 U+1F1F7' },
'BS' => { 'emoji' => '๐ง๐ธ', 'unicode' => 'U+1F1E7 U+1F1F8' },
'BT' => { 'emoji' => '๐ง๐น', 'unicode' => 'U+1F1E7 U+1F1F9' },
'BV' => { 'emoji' => '๐ง๐ป', 'unicode' => 'U+1F1E7 U+1F1FB' },
'BW' => { 'emoji' => '๐ง๐ผ', 'unicode' => 'U+1F1E7 U+1F1FC' },
'BY' => { 'emoji' => '๐ง๐พ', 'unicode' => 'U+1F1E7 U+1F1FE' },
'BZ' => { 'emoji' => '๐ง๐ฟ', 'unicode' => 'U+1F1E7 U+1F1FF' },
'CA' => { 'emoji' => '๐จ๐ฆ', 'unicode' => 'U+1F1E8 U+1F1E6' },
'CC' => { 'emoji' => '๐จ๐จ', 'unicode' => 'U+1F1E8 U+1F1E8' },
'CD' => { 'emoji' => '๐จ๐ฉ', 'unicode' => 'U+1F1E8 U+1F1E9' },
'CF' => { 'emoji' => '๐จ๐ซ', 'unicode' => 'U+1F1E8 U+1F1EB' },
'CG' => { 'emoji' => '๐จ๐ฌ', 'unicode' => 'U+1F1E8 U+1F1EC' },
'CH' => { 'emoji' => '๐จ๐ญ', 'unicode' => 'U+1F1E8 U+1F1ED' },
'CI' => { 'emoji' => '๐จ๐ฎ', 'unicode' => 'U+1F1E8 U+1F1EE' },
'CK' => { 'emoji' => '๐จ๐ฐ', 'unicode' => 'U+1F1E8 U+1F1F0' },
'CL' => { 'emoji' => '๐จ๐ฑ', 'unicode' => 'U+1F1E8 U+1F1F1' },
'CM' => { 'emoji' => '๐จ๐ฒ', 'unicode' => 'U+1F1E8 U+1F1F2' },
'CN' => { 'emoji' => '๐จ๐ณ', 'unicode' => 'U+1F1E8 U+1F1F3' },
'CO' => { 'emoji' => '๐จ๐ด', 'unicode' => 'U+1F1E8 U+1F1F4' },
'CR' => { 'emoji' => '๐จ๐ท', 'unicode' => 'U+1F1E8 U+1F1F7' },
'CU' => { 'emoji' => '๐จ๐บ', 'unicode' => 'U+1F1E8 U+1F1FA' },
'CV' => { 'emoji' => '๐จ๐ป', 'unicode' => 'U+1F1E8 U+1F1FB' },
'CW' => { 'emoji' => '๐จ๐ผ', 'unicode' => 'U+1F1E8 U+1F1FC' },
'CX' => { 'emoji' => '๐จ๐ฝ', 'unicode' => 'U+1F1E8 U+1F1FD' },
'CY' => { 'emoji' => '๐จ๐พ', 'unicode' => 'U+1F1E8 U+1F1FE' },
'CZ' => { 'emoji' => '๐จ๐ฟ', 'unicode' => 'U+1F1E8 U+1F1FF' },
'DE' => { 'emoji' => '๐ฉ๐ช', 'unicode' => 'U+1F1E9 U+1F1EA' },
'DJ' => { 'emoji' => '๐ฉ๐ฏ', 'unicode' => 'U+1F1E9 U+1F1EF' },
'DK' => { 'emoji' => '๐ฉ๐ฐ', 'unicode' => 'U+1F1E9 U+1F1F0' },
'DM' => { 'emoji' => '๐ฉ๐ฒ', 'unicode' => 'U+1F1E9 U+1F1F2' },
'DO' => { 'emoji' => '๐ฉ๐ด', 'unicode' => 'U+1F1E9 U+1F1F4' },
'DZ' => { 'emoji' => '๐ฉ๐ฟ', 'unicode' => 'U+1F1E9 U+1F1FF' },
'EC' => { 'emoji' => '๐ช๐จ', 'unicode' => 'U+1F1EA U+1F1E8' },
'EE' => { 'emoji' => '๐ช๐ช', 'unicode' => 'U+1F1EA U+1F1EA' },
'EG' => { 'emoji' => '๐ช๐ฌ', 'unicode' => 'U+1F1EA U+1F1EC' },
'EH' => { 'emoji' => '๐ช๐ญ', 'unicode' => 'U+1F1EA U+1F1ED' },
'ER' => { 'emoji' => '๐ช๐ท', 'unicode' => 'U+1F1EA U+1F1F7' },
'ES' => { 'emoji' => '๐ช๐ธ', 'unicode' => 'U+1F1EA U+1F1F8' },
'ET' => { 'emoji' => '๐ช๐น', 'unicode' => 'U+1F1EA U+1F1F9' },
'FI' => { 'emoji' => '๐ซ๐ฎ', 'unicode' => 'U+1F1EB U+1F1EE' },
'FJ' => { 'emoji' => '๐ซ๐ฏ', 'unicode' => 'U+1F1EB U+1F1EF' },
'FK' => { 'emoji' => '๐ซ๐ฐ', 'unicode' => 'U+1F1EB U+1F1F0' },
'FM' => { 'emoji' => '๐ซ๐ฒ', 'unicode' => 'U+1F1EB U+1F1F2' },
'FO' => { 'emoji' => '๐ซ๐ด', 'unicode' => 'U+1F1EB U+1F1F4' },
'FR' => { 'emoji' => '๐ซ๐ท', 'unicode' => 'U+1F1EB U+1F1F7' },
'GA' => { 'emoji' => '๐ฌ๐ฆ', 'unicode' => 'U+1F1EC U+1F1E6' },
'GB' => { 'emoji' => '๐ฌ๐ง', 'unicode' => 'U+1F1EC U+1F1E7' },
'GD' => { 'emoji' => '๐ฌ๐ฉ', 'unicode' => 'U+1F1EC U+1F1E9' },
'GE' => { 'emoji' => '๐ฌ๐ช', 'unicode' => 'U+1F1EC U+1F1EA' },
'GF' => { 'emoji' => '๐ฌ๐ซ', 'unicode' => 'U+1F1EC U+1F1EB' },
'GG' => { 'emoji' => '๐ฌ๐ฌ', 'unicode' => 'U+1F1EC U+1F1EC' },
'GH' => { 'emoji' => '๐ฌ๐ญ', 'unicode' => 'U+1F1EC U+1F1ED' },
'GI' => { 'emoji' => '๐ฌ๐ฎ', 'unicode' => 'U+1F1EC U+1F1EE' },
'GL' => { 'emoji' => '๐ฌ๐ฑ', 'unicode' => 'U+1F1EC U+1F1F1' },
'GM' => { 'emoji' => '๐ฌ๐ฒ', 'unicode' => 'U+1F1EC U+1F1F2' },
'GN' => { 'emoji' => '๐ฌ๐ณ', 'unicode' => 'U+1F1EC U+1F1F3' },
'GP' => { 'emoji' => '๐ฌ๐ต', 'unicode' => 'U+1F1EC U+1F1F5' },
'GQ' => { 'emoji' => '๐ฌ๐ถ', 'unicode' => 'U+1F1EC U+1F1F6' },
'GR' => { 'emoji' => '๐ฌ๐ท', 'unicode' => 'U+1F1EC U+1F1F7' },
'GS' => { 'emoji' => '๐ฌ๐ธ', 'unicode' => 'U+1F1EC U+1F1F8' },
'GT' => { 'emoji' => '๐ฌ๐น', 'unicode' => 'U+1F1EC U+1F1F9' },
'GU' => { 'emoji' => '๐ฌ๐บ', 'unicode' => 'U+1F1EC U+1F1FA' },
'GW' => { 'emoji' => '๐ฌ๐ผ', 'unicode' => 'U+1F1EC U+1F1FC' },
'GY' => { 'emoji' => '๐ฌ๐พ', 'unicode' => 'U+1F1EC U+1F1FE' },
'HK' => { 'emoji' => '๐ญ๐ฐ', 'unicode' => 'U+1F1ED U+1F1F0' },
'HM' => { 'emoji' => '๐ญ๐ฒ', 'unicode' => 'U+1F1ED U+1F1F2' },
'HN' => { 'emoji' => '๐ญ๐ณ', 'unicode' => 'U+1F1ED U+1F1F3' },
'HR' => { 'emoji' => '๐ญ๐ท', 'unicode' => 'U+1F1ED U+1F1F7' },
'HT' => { 'emoji' => '๐ญ๐น', 'unicode' => 'U+1F1ED U+1F1F9' },
'HU' => { 'emoji' => '๐ญ๐บ', 'unicode' => 'U+1F1ED U+1F1FA' },
'ID' => { 'emoji' => '๐ฎ๐ฉ', 'unicode' => 'U+1F1EE U+1F1E9' },
'IE' => { 'emoji' => '๐ฎ๐ช', 'unicode' => 'U+1F1EE U+1F1EA' },
'IL' => { 'emoji' => '๐ฎ๐ฑ', 'unicode' => 'U+1F1EE U+1F1F1' },
'IM' => { 'emoji' => '๐ฎ๐ฒ', 'unicode' => 'U+1F1EE U+1F1F2' },
'IN' => { 'emoji' => '๐ฎ๐ณ', 'unicode' => 'U+1F1EE U+1F1F3' },
'IO' => { 'emoji' => '๐ฎ๐ด', 'unicode' => 'U+1F1EE U+1F1F4' },
'IQ' => { 'emoji' => '๐ฎ๐ถ', 'unicode' => 'U+1F1EE U+1F1F6' },
'IR' => { 'emoji' => '๐ฎ๐ท', 'unicode' => 'U+1F1EE U+1F1F7' },
'IS' => { 'emoji' => '๐ฎ๐ธ', 'unicode' => 'U+1F1EE U+1F1F8' },
'IT' => { 'emoji' => '๐ฎ๐น', 'unicode' => 'U+1F1EE U+1F1F9' },
'JE' => { 'emoji' => '๐ฏ๐ช', 'unicode' => 'U+1F1EF U+1F1EA' },
'JM' => { 'emoji' => '๐ฏ๐ฒ', 'unicode' => 'U+1F1EF U+1F1F2' },
'JO' => { 'emoji' => '๐ฏ๐ด', 'unicode' => 'U+1F1EF U+1F1F4' },
'JP' => { 'emoji' => '๐ฏ๐ต', 'unicode' => 'U+1F1EF U+1F1F5' },
'KE' => { 'emoji' => '๐ฐ๐ช', 'unicode' => 'U+1F1F0 U+1F1EA' },
'KG' => { 'emoji' => '๐ฐ๐ฌ', 'unicode' => 'U+1F1F0 U+1F1EC' },
'KH' => { 'emoji' => '๐ฐ๐ญ', 'unicode' => 'U+1F1F0 U+1F1ED' },
'KI' => { 'emoji' => '๐ฐ๐ฎ', 'unicode' => 'U+1F1F0 U+1F1EE' },
'KM' => { 'emoji' => '๐ฐ๐ฒ', 'unicode' => 'U+1F1F0 U+1F1F2' },
'KN' => { 'emoji' => '๐ฐ๐ณ', 'unicode' => 'U+1F1F0 U+1F1F3' },
'KP' => { 'emoji' => '๐ฐ๐ต', 'unicode' => 'U+1F1F0 U+1F1F5' },
'KR' => { 'emoji' => '๐ฐ๐ท', 'unicode' => 'U+1F1F0 U+1F1F7' },
'KW' => { 'emoji' => '๐ฐ๐ผ', 'unicode' => 'U+1F1F0 U+1F1FC' },
'KY' => { 'emoji' => '๐ฐ๐พ', 'unicode' => 'U+1F1F0 U+1F1FE' },
'KZ' => { 'emoji' => '๐ฐ๐ฟ', 'unicode' => 'U+1F1F0 U+1F1FF' },
'LA' => { 'emoji' => '๐ฑ๐ฆ', 'unicode' => 'U+1F1F1 U+1F1E6' },
'LB' => { 'emoji' => '๐ฑ๐ง', 'unicode' => 'U+1F1F1 U+1F1E7' },
'LC' => { 'emoji' => '๐ฑ๐จ', 'unicode' => 'U+1F1F1 U+1F1E8' },
'LI' => { 'emoji' => '๐ฑ๐ฎ', 'unicode' => 'U+1F1F1 U+1F1EE' },
'LK' => { 'emoji' => '๐ฑ๐ฐ', 'unicode' => 'U+1F1F1 U+1F1F0' },
'LR' => { 'emoji' => '๐ฑ๐ท', 'unicode' => 'U+1F1F1 U+1F1F7' },
'LS' => { 'emoji' => '๐ฑ๐ธ', 'unicode' => 'U+1F1F1 U+1F1F8' },
'LT' => { 'emoji' => '๐ฑ๐น', 'unicode' => 'U+1F1F1 U+1F1F9' },
'LU' => { 'emoji' => '๐ฑ๐บ', 'unicode' => 'U+1F1F1 U+1F1FA' },
'LV' => { 'emoji' => '๐ฑ๐ป', 'unicode' => 'U+1F1F1 U+1F1FB' },
'LY' => { 'emoji' => '๐ฑ๐พ', 'unicode' => 'U+1F1F1 U+1F1FE' },
'MA' => { 'emoji' => '๐ฒ๐ฆ', 'unicode' => 'U+1F1F2 U+1F1E6' },
'MC' => { 'emoji' => '๐ฒ๐จ', 'unicode' => 'U+1F1F2 U+1F1E8' },
'MD' => { 'emoji' => '๐ฒ๐ฉ', 'unicode' => 'U+1F1F2 U+1F1E9' },
'ME' => { 'emoji' => '๐ฒ๐ช', 'unicode' => 'U+1F1F2 U+1F1EA' },
'MF' => { 'emoji' => '๐ฒ๐ซ', 'unicode' => 'U+1F1F2 U+1F1EB' },
'MG' => { 'emoji' => '๐ฒ๐ฌ', 'unicode' => 'U+1F1F2 U+1F1EC' },
'MH' => { 'emoji' => '๐ฒ๐ญ', 'unicode' => 'U+1F1F2 U+1F1ED' },
'MK' => { 'emoji' => '๐ฒ๐ฐ', 'unicode' => 'U+1F1F2 U+1F1F0' },
'ML' => { 'emoji' => '๐ฒ๐ฑ', 'unicode' => 'U+1F1F2 U+1F1F1' },
'MM' => { 'emoji' => '๐ฒ๐ฒ', 'unicode' => 'U+1F1F2 U+1F1F2' },
'MN' => { 'emoji' => '๐ฒ๐ณ', 'unicode' => 'U+1F1F2 U+1F1F3' },
'MO' => { 'emoji' => '๐ฒ๐ด', 'unicode' => 'U+1F1F2 U+1F1F4' },
'MP' => { 'emoji' => '๐ฒ๐ต', 'unicode' => 'U+1F1F2 U+1F1F5' },
'MQ' => { 'emoji' => '๐ฒ๐ถ', 'unicode' => 'U+1F1F2 U+1F1F6' },
'MR' => { 'emoji' => '๐ฒ๐ท', 'unicode' => 'U+1F1F2 U+1F1F7' },
'MS' => { 'emoji' => '๐ฒ๐ธ', 'unicode' => 'U+1F1F2 U+1F1F8' },
'MT' => { 'emoji' => '๐ฒ๐น', 'unicode' => 'U+1F1F2 U+1F1F9' },
'MU' => { 'emoji' => '๐ฒ๐บ', 'unicode' => 'U+1F1F2 U+1F1FA' },
'MV' => { 'emoji' => '๐ฒ๐ป', 'unicode' => 'U+1F1F2 U+1F1FB' },
'MW' => { 'emoji' => '๐ฒ๐ผ', 'unicode' => 'U+1F1F2 U+1F1FC' },
'MX' => { 'emoji' => '๐ฒ๐ฝ', 'unicode' => 'U+1F1F2 U+1F1FD' },
'MY' => { 'emoji' => '๐ฒ๐พ', 'unicode' => 'U+1F1F2 U+1F1FE' },
'MZ' => { 'emoji' => '๐ฒ๐ฟ', 'unicode' => 'U+1F1F2 U+1F1FF' },
'NA' => { 'emoji' => '๐ณ๐ฆ', 'unicode' => 'U+1F1F3 U+1F1E6' },
'NC' => { 'emoji' => '๐ณ๐จ', 'unicode' => 'U+1F1F3 U+1F1E8' },
'NE' => { 'emoji' => '๐ณ๐ช', 'unicode' => 'U+1F1F3 U+1F1EA' },
'NF' => { 'emoji' => '๐ณ๐ซ', 'unicode' => 'U+1F1F3 U+1F1EB' },
'NG' => { 'emoji' => '๐ณ๐ฌ', 'unicode' => 'U+1F1F3 U+1F1EC' },
'NI' => { 'emoji' => '๐ณ๐ฎ', 'unicode' => 'U+1F1F3 U+1F1EE' },
'NL' => { 'emoji' => '๐ณ๐ฑ', 'unicode' => 'U+1F1F3 U+1F1F1' },
'NO' => { 'emoji' => '๐ณ๐ด', 'unicode' => 'U+1F1F3 U+1F1F4' },
'NP' => { 'emoji' => '๐ณ๐ต', 'unicode' => 'U+1F1F3 U+1F1F5' },
'NR' => { 'emoji' => '๐ณ๐ท', 'unicode' => 'U+1F1F3 U+1F1F7' },
'NU' => { 'emoji' => '๐ณ๐บ', 'unicode' => 'U+1F1F3 U+1F1FA' },
'NZ' => { 'emoji' => '๐ณ๐ฟ', 'unicode' => 'U+1F1F3 U+1F1FF' },
'OM' => { 'emoji' => '๐ด๐ฒ', 'unicode' => 'U+1F1F4 U+1F1F2' },
'PA' => { 'emoji' => '๐ต๐ฆ', 'unicode' => 'U+1F1F5 U+1F1E6' },
'PE' => { 'emoji' => '๐ต๐ช', 'unicode' => 'U+1F1F5 U+1F1EA' },
'PF' => { 'emoji' => '๐ต๐ซ', 'unicode' => 'U+1F1F5 U+1F1EB' },
'PG' => { 'emoji' => '๐ต๐ฌ', 'unicode' => 'U+1F1F5 U+1F1EC' },
'PH' => { 'emoji' => '๐ต๐ญ', 'unicode' => 'U+1F1F5 U+1F1ED' },
'PK' => { 'emoji' => '๐ต๐ฐ', 'unicode' => 'U+1F1F5 U+1F1F0' },
'PL' => { 'emoji' => '๐ต๐ฑ', 'unicode' => 'U+1F1F5 U+1F1F1' },
'PM' => { 'emoji' => '๐ต๐ฒ', 'unicode' => 'U+1F1F5 U+1F1F2' },
'PN' => { 'emoji' => '๐ต๐ณ', 'unicode' => 'U+1F1F5 U+1F1F3' },
'PR' => { 'emoji' => '๐ต๐ท', 'unicode' => 'U+1F1F5 U+1F1F7' },
'PS' => { 'emoji' => '๐ต๐ธ', 'unicode' => 'U+1F1F5 U+1F1F8' },
'PT' => { 'emoji' => '๐ต๐น', 'unicode' => 'U+1F1F5 U+1F1F9' },
'PW' => { 'emoji' => '๐ต๐ผ', 'unicode' => 'U+1F1F5 U+1F1FC' },
'PY' => { 'emoji' => '๐ต๐พ', 'unicode' => 'U+1F1F5 U+1F1FE' },
'QA' => { 'emoji' => '๐ถ๐ฆ', 'unicode' => 'U+1F1F6 U+1F1E6' },
'RE' => { 'emoji' => '๐ท๐ช', 'unicode' => 'U+1F1F7 U+1F1EA' },
'RO' => { 'emoji' => '๐ท๐ด', 'unicode' => 'U+1F1F7 U+1F1F4' },
'RS' => { 'emoji' => '๐ท๐ธ', 'unicode' => 'U+1F1F7 U+1F1F8' },
'RU' => { 'emoji' => '๐ท๐บ', 'unicode' => 'U+1F1F7 U+1F1FA' },
'RW' => { 'emoji' => '๐ท๐ผ', 'unicode' => 'U+1F1F7 U+1F1FC' },
'SA' => { 'emoji' => '๐ธ๐ฆ', 'unicode' => 'U+1F1F8 U+1F1E6' },
'SB' => { 'emoji' => '๐ธ๐ง', 'unicode' => 'U+1F1F8 U+1F1E7' },
'SC' => { 'emoji' => '๐ธ๐จ', 'unicode' => 'U+1F1F8 U+1F1E8' },
'SD' => { 'emoji' => '๐ธ๐ฉ', 'unicode' => 'U+1F1F8 U+1F1E9' },
'SE' => { 'emoji' => '๐ธ๐ช', 'unicode' => 'U+1F1F8 U+1F1EA' },
'SG' => { 'emoji' => '๐ธ๐ฌ', 'unicode' => 'U+1F1F8 U+1F1EC' },
'SH' => { 'emoji' => '๐ธ๐ญ', 'unicode' => 'U+1F1F8 U+1F1ED' },
'SI' => { 'emoji' => '๐ธ๐ฎ', 'unicode' => 'U+1F1F8 U+1F1EE' },
'SJ' => { 'emoji' => '๐ธ๐ฏ', 'unicode' => 'U+1F1F8 U+1F1EF' },
'SK' => { 'emoji' => '๐ธ๐ฐ', 'unicode' => 'U+1F1F8 U+1F1F0' },
'SL' => { 'emoji' => '๐ธ๐ฑ', 'unicode' => 'U+1F1F8 U+1F1F1' },
'SM' => { 'emoji' => '๐ธ๐ฒ', 'unicode' => 'U+1F1F8 U+1F1F2' },
'SN' => { 'emoji' => '๐ธ๐ณ', 'unicode' => 'U+1F1F8 U+1F1F3' },
'SO' => { 'emoji' => '๐ธ๐ด', 'unicode' => 'U+1F1F8 U+1F1F4' },
'SR' => { 'emoji' => '๐ธ๐ท', 'unicode' => 'U+1F1F8 U+1F1F7' },
'SS' => { 'emoji' => '๐ธ๐ธ', 'unicode' => 'U+1F1F8 U+1F1F8' },
'ST' => { 'emoji' => '๐ธ๐น', 'unicode' => 'U+1F1F8 U+1F1F9' },
'SV' => { 'emoji' => '๐ธ๐ป', 'unicode' => 'U+1F1F8 U+1F1FB' },
'SX' => { 'emoji' => '๐ธ๐ฝ', 'unicode' => 'U+1F1F8 U+1F1FD' },
'SY' => { 'emoji' => '๐ธ๐พ', 'unicode' => 'U+1F1F8 U+1F1FE' },
'SZ' => { 'emoji' => '๐ธ๐ฟ', 'unicode' => 'U+1F1F8 U+1F1FF' },
'TC' => { 'emoji' => '๐น๐จ', 'unicode' => 'U+1F1F9 U+1F1E8' },
'TD' => { 'emoji' => '๐น๐ฉ', 'unicode' => 'U+1F1F9 U+1F1E9' },
'TF' => { 'emoji' => '๐น๐ซ', 'unicode' => 'U+1F1F9 U+1F1EB' },
'TG' => { 'emoji' => '๐น๐ฌ', 'unicode' => 'U+1F1F9 U+1F1EC' },
'TH' => { 'emoji' => '๐น๐ญ', 'unicode' => 'U+1F1F9 U+1F1ED' },
'TJ' => { 'emoji' => '๐น๐ฏ', 'unicode' => 'U+1F1F9 U+1F1EF' },
'TK' => { 'emoji' => '๐น๐ฐ', 'unicode' => 'U+1F1F9 U+1F1F0' },
'TL' => { 'emoji' => '๐น๐ฑ', 'unicode' => 'U+1F1F9 U+1F1F1' },
'TM' => { 'emoji' => '๐น๐ฒ', 'unicode' => 'U+1F1F9 U+1F1F2' },
'TN' => { 'emoji' => '๐น๐ณ', 'unicode' => 'U+1F1F9 U+1F1F3' },
'TO' => { 'emoji' => '๐น๐ด', 'unicode' => 'U+1F1F9 U+1F1F4' },
'TR' => { 'emoji' => '๐น๐ท', 'unicode' => 'U+1F1F9 U+1F1F7' },
'TT' => { 'emoji' => '๐น๐น', 'unicode' => 'U+1F1F9 U+1F1F9' },
'TV' => { 'emoji' => '๐น๐ป', 'unicode' => 'U+1F1F9 U+1F1FB' },
'TW' => { 'emoji' => '๐น๐ผ', 'unicode' => 'U+1F1F9 U+1F1FC' },
'TZ' => { 'emoji' => '๐น๐ฟ', 'unicode' => 'U+1F1F9 U+1F1FF' },
'UA' => { 'emoji' => '๐บ๐ฆ', 'unicode' => 'U+1F1FA U+1F1E6' },
'UG' => { 'emoji' => '๐บ๐ฌ', 'unicode' => 'U+1F1FA U+1F1EC' },
'UM' => { 'emoji' => '๐บ๐ฒ', 'unicode' => 'U+1F1FA U+1F1F2' },
'US' => { 'emoji' => '๐บ๐ธ', 'unicode' => 'U+1F1FA U+1F1F8' },
'UY' => { 'emoji' => '๐บ๐พ', 'unicode' => 'U+1F1FA U+1F1FE' },
'UZ' => { 'emoji' => '๐บ๐ฟ', 'unicode' => 'U+1F1FA U+1F1FF' },
'VA' => { 'emoji' => '๐ป๐ฆ', 'unicode' => 'U+1F1FB U+1F1E6' },
'VC' => { 'emoji' => '๐ป๐จ', 'unicode' => 'U+1F1FB U+1F1E8' },
'VE' => { 'emoji' => '๐ป๐ช', 'unicode' => 'U+1F1FB U+1F1EA' },
'VG' => { 'emoji' => '๐ป๐ฌ', 'unicode' => 'U+1F1FB U+1F1EC' },
'VI' => { 'emoji' => '๐ป๐ฎ', 'unicode' => 'U+1F1FB U+1F1EE' },
'VN' => { 'emoji' => '๐ป๐ณ', 'unicode' => 'U+1F1FB U+1F1F3' },
'VU' => { 'emoji' => '๐ป๐บ', 'unicode' => 'U+1F1FB U+1F1FA' },
'WF' => { 'emoji' => '๐ผ๐ซ', 'unicode' => 'U+1F1FC U+1F1EB' },
'WS' => { 'emoji' => '๐ผ๐ธ', 'unicode' => 'U+1F1FC U+1F1F8' },
'XK' => { 'emoji' => '๐ฝ๐ฐ', 'unicode' => 'U+1F1FD U+1F1F0' },
'YE' => { 'emoji' => '๐พ๐ช', 'unicode' => 'U+1F1FE U+1F1EA' },
'YT' => { 'emoji' => '๐พ๐น', 'unicode' => 'U+1F1FE U+1F1F9' },
'ZA' => { 'emoji' => '๐ฟ๐ฆ', 'unicode' => 'U+1F1FF U+1F1E6' },
'ZM' => { 'emoji' => '๐ฟ๐ฒ', 'unicode' => 'U+1F1FF U+1F1F2' },
'ZW' => { 'emoji' => '๐ฟ๐ผ', 'unicode' => 'U+1F1FF U+1F1FC' }
);
my @default_eu_countries = (
"IE", "AT", "LT", "LU", "LV", "DE", "DK", "SE", "SI", "SK",
"CZ", "CY", "NL", "FI", "FR", "MT", "ES", "IT", "EE", "PL",
"PT", "HU", "HR", "GR", "RO", "BG", "BE"
);
my %default_countries_currencies = (
'AD' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'AE' => { 'code' => 'AED', 'symbol' => 'ุฏ.ุฅ' },
'AF' => { 'code' => 'AFN', 'symbol' => 'ุ' },
'AG' => { 'code' => 'XCD', 'symbol' => '$' },
'AI' => { 'code' => 'XCD', 'symbol' => '$' },
'AL' => { 'code' => 'ALL', 'symbol' => 'L' },
'AM' => { 'code' => 'AMD', 'symbol' => 'ึ' },
'AO' => { 'code' => 'AOA', 'symbol' => 'Kz' },
'AQ' => { 'code' => '', 'symbol' => '$' },
'AR' => { 'code' => 'ARS', 'symbol' => '$' },
'AS' => { 'code' => 'USD', 'symbol' => '$' },
'AT' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'AU' => { 'code' => 'AUD', 'symbol' => '$' },
'AW' => { 'code' => 'AWG', 'symbol' => 'ฦ' },
'AX' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'AZ' => { 'code' => 'AZN', 'symbol' => 'โผ' },
'BA' => { 'code' => 'BAM', 'symbol' => 'KM' },
'BB' => { 'code' => 'BBD', 'symbol' => '$' },
'BD' => { 'code' => 'BDT', 'symbol' => 'เงณ' },
'BE' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'BF' => { 'code' => 'XOF', 'symbol' => 'CFA' },
'BG' => { 'code' => 'BGN', 'symbol' => 'ะปะฒ' },
'BH' => { 'code' => 'BHD', 'symbol' => '.ุฏ.ุจ' },
'BI' => { 'code' => 'BIF', 'symbol' => 'FBu' },
'BJ' => { 'code' => 'XOF', 'symbol' => 'CFA' },
'BL' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'BM' => { 'code' => 'BMD', 'symbol' => '$' },
'BN' => { 'code' => 'BND', 'symbol' => '$' },
'BO' => { 'code' => 'BOB', 'symbol' => '$b' },
'BQ' => { 'code' => 'USD', 'symbol' => '$' },
'BR' => { 'code' => 'BRL', 'symbol' => 'R$' },
'BS' => { 'code' => 'BSD', 'symbol' => '$' },
'BT' => { 'code' => 'BTN', 'symbol' => 'Nu.' },
'BV' => { 'code' => 'NOK', 'symbol' => 'kr' },
'BW' => { 'code' => 'BWP', 'symbol' => 'P' },
'BY' => { 'code' => 'BYR', 'symbol' => 'Br' },
'BZ' => { 'code' => 'BZD', 'symbol' => 'BZ$' },
'CA' => { 'code' => 'CAD', 'symbol' => '$' },
'CC' => { 'code' => 'AUD', 'symbol' => '$' },
'CD' => { 'code' => 'CDF', 'symbol' => 'FC' },
'CF' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
'CG' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
'CH' => { 'code' => 'CHF', 'symbol' => 'CHF' },
'CI' => { 'code' => 'XOF', 'symbol' => 'CFA' },
'CK' => { 'code' => 'NZD', 'symbol' => '$' },
'CL' => { 'code' => 'CLP', 'symbol' => '$' },
'CM' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
'CN' => { 'code' => 'CNY', 'symbol' => 'ยฅ' },
'CO' => { 'code' => 'COP', 'symbol' => '$' },
'CR' => { 'code' => 'CRC', 'symbol' => 'โก' },
'CU' => { 'code' => 'CUP', 'symbol' => 'โฑ' },
'CV' => { 'code' => 'CVE', 'symbol' => '$' },
'CW' => { 'code' => 'ANG', 'symbol' => 'ฦ' },
'CX' => { 'code' => 'AUD', 'symbol' => '$' },
'CY' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'CZ' => { 'code' => 'CZK', 'symbol' => 'Kฤ' },
'DE' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'DJ' => { 'code' => 'DJF', 'symbol' => 'Fdj' },
'DK' => { 'code' => 'DKK', 'symbol' => 'kr' },
'DM' => { 'code' => 'XCD', 'symbol' => '$' },
'DO' => { 'code' => 'DOP', 'symbol' => 'RD$' },
'DZ' => { 'code' => 'DZD', 'symbol' => 'ุฏุฌ' },
'EC' => { 'code' => 'USD', 'symbol' => '$' },
'EE' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'EG' => { 'code' => 'EGP', 'symbol' => 'ยฃ' },
'EH' => { 'code' => 'MAD', 'symbol' => 'MAD' },
'ER' => { 'code' => 'ERN', 'symbol' => 'Nfk' },
'ES' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'ET' => { 'code' => 'ETB', 'symbol' => 'Br' },
'FI' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'FJ' => { 'code' => 'FJD', 'symbol' => '$' },
'FK' => { 'code' => 'FKP', 'symbol' => 'ยฃ' },
'FM' => { 'code' => 'USD', 'symbol' => '$' },
'FO' => { 'code' => 'DKK', 'symbol' => 'kr' },
'FR' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'GA' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
'GB' => { 'code' => 'GBP', 'symbol' => 'ยฃ' },
'GD' => { 'code' => 'XCD', 'symbol' => '$' },
'GE' => { 'code' => 'GEL', 'symbol' => 'แ' },
'GF' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'GG' => { 'code' => 'GBP', 'symbol' => 'ยฃ' },
'GH' => { 'code' => 'GHS', 'symbol' => 'GHโต' },
'GI' => { 'code' => 'GIP', 'symbol' => 'ยฃ' },
'GL' => { 'code' => 'DKK', 'symbol' => 'kr' },
'GM' => { 'code' => 'GMD', 'symbol' => 'D' },
'GN' => { 'code' => 'GNF', 'symbol' => 'FG' },
'GP' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'GQ' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
'GR' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'GS' => { 'code' => 'GBP', 'symbol' => 'ยฃ' },
'GT' => { 'code' => 'GTQ', 'symbol' => 'Q' },
'GU' => { 'code' => 'USD', 'symbol' => '$' },
'GW' => { 'code' => 'XOF', 'symbol' => 'CFA' },
'GY' => { 'code' => 'GYD', 'symbol' => '$' },
'HK' => { 'code' => 'HKD', 'symbol' => '$' },
'HM' => { 'code' => 'AUD', 'symbol' => '$' },
'HN' => { 'code' => 'HNL', 'symbol' => 'L' },
'HR' => { 'code' => 'HRK', 'symbol' => 'kn' },
'HT' => { 'code' => 'HTG', 'symbol' => 'G' },
'HU' => { 'code' => 'HUF', 'symbol' => 'Ft' },
'ID' => { 'code' => 'IDR', 'symbol' => 'Rp' },
'IE' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'IL' => { 'code' => 'ILS', 'symbol' => 'โช' },
'IM' => { 'code' => 'GBP', 'symbol' => 'ยฃ' },
'IN' => { 'code' => 'INR', 'symbol' => 'โน' },
'IO' => { 'code' => 'USD', 'symbol' => '$' },
'IQ' => { 'code' => 'IQD', 'symbol' => 'ุน.ุฏ' },
'IR' => { 'code' => 'IRR', 'symbol' => '๏ทผ' },
'IS' => { 'code' => 'ISK', 'symbol' => 'kr' },
'IT' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'JE' => { 'code' => 'GBP', 'symbol' => 'ยฃ' },
'JM' => { 'code' => 'JMD', 'symbol' => 'J$' },
'JO' => { 'code' => 'JOD', 'symbol' => 'JD' },
'JP' => { 'code' => 'JPY', 'symbol' => 'ยฅ' },
'KE' => { 'code' => 'KES', 'symbol' => 'KSh' },
'KG' => { 'code' => 'KGS', 'symbol' => 'ะปะฒ' },
'KH' => { 'code' => 'KHR', 'symbol' => 'แ' },
'KI' => { 'code' => 'AUD', 'symbol' => '$' },
'KM' => { 'code' => 'KMF', 'symbol' => 'CF' },
'KN' => { 'code' => 'XCD', 'symbol' => '$' },
'KP' => { 'code' => 'KPW', 'symbol' => 'โฉ' },
'KR' => { 'code' => 'KRW', 'symbol' => 'โฉ' },
'KW' => { 'code' => 'KWD', 'symbol' => 'KD' },
'KY' => { 'code' => 'KYD', 'symbol' => '$' },
'KZ' => { 'code' => 'KZT', 'symbol' => 'โธ' },
'LA' => { 'code' => 'LAK', 'symbol' => 'โญ' },
'LB' => { 'code' => 'LBP', 'symbol' => 'ยฃ' },
'LC' => { 'code' => 'XCD', 'symbol' => '$' },
'LI' => { 'code' => 'CHF', 'symbol' => 'CHF' },
'LK' => { 'code' => 'LKR', 'symbol' => 'โจ' },
'LR' => { 'code' => 'LRD', 'symbol' => '$' },
'LS' => { 'code' => 'LSL', 'symbol' => 'M' },
'LT' => { 'code' => 'LTL', 'symbol' => 'Lt' },
'LU' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'LV' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'LY' => { 'code' => 'LYD', 'symbol' => 'LD' },
'MA' => { 'code' => 'MAD', 'symbol' => 'MAD' },
'MC' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'MD' => { 'code' => 'MDL', 'symbol' => 'lei' },
'ME' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'MF' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'MG' => { 'code' => 'MGA', 'symbol' => 'Ar' },
'MH' => { 'code' => 'USD', 'symbol' => '$' },
'MK' => { 'code' => 'MKD', 'symbol' => 'ะดะตะฝ' },
'ML' => { 'code' => 'XOF', 'symbol' => 'CFA' },
'MM' => { 'code' => 'MMK', 'symbol' => 'K' },
'MN' => { 'code' => 'MNT', 'symbol' => 'โฎ' },
'MO' => { 'code' => 'MOP', 'symbol' => 'MOP$' },
'MP' => { 'code' => 'USD', 'symbol' => '$' },
'MQ' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'MR' => { 'code' => 'MRO', 'symbol' => 'UM' },
'MS' => { 'code' => 'XCD', 'symbol' => '$' },
'MT' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'MU' => { 'code' => 'MUR', 'symbol' => 'โจ' },
'MV' => { 'code' => 'MVR', 'symbol' => 'Rf' },
'MW' => { 'code' => 'MWK', 'symbol' => 'MK' },
'MX' => { 'code' => 'MXN', 'symbol' => '$' },
'MY' => { 'code' => 'MYR', 'symbol' => 'RM' },
'MZ' => { 'code' => 'MZN', 'symbol' => 'MT' },
'NA' => { 'code' => 'NAD', 'symbol' => '$' },
'NC' => { 'code' => 'XPF', 'symbol' => 'โฃ' },
'NE' => { 'code' => 'XOF', 'symbol' => 'CFA' },
'NF' => { 'code' => 'AUD', 'symbol' => '$' },
'NG' => { 'code' => 'NGN', 'symbol' => 'โฆ' },
'NI' => { 'code' => 'NIO', 'symbol' => 'C$' },
'NL' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'NO' => { 'code' => 'NOK', 'symbol' => 'kr' },
'NP' => { 'code' => 'NPR', 'symbol' => 'โจ' },
'NR' => { 'code' => 'AUD', 'symbol' => '$' },
'NU' => { 'code' => 'NZD', 'symbol' => '$' },
'NZ' => { 'code' => 'NZD', 'symbol' => '$' },
'OM' => { 'code' => 'OMR', 'symbol' => '๏ทผ' },
'PA' => { 'code' => 'PAB', 'symbol' => 'B/.' },
'PE' => { 'code' => 'PEN', 'symbol' => 'S/.' },
'PF' => { 'code' => 'XPF', 'symbol' => 'โฃ' },
'PG' => { 'code' => 'PGK', 'symbol' => 'K' },
'PH' => { 'code' => 'PHP', 'symbol' => 'โฑ' },
'PK' => { 'code' => 'PKR', 'symbol' => 'โจ' },
'PL' => { 'code' => 'PLN', 'symbol' => 'zล' },
'PM' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'PN' => { 'code' => 'NZD', 'symbol' => '$' },
'PR' => { 'code' => 'USD', 'symbol' => '$' },
'PS' => { 'code' => 'ILS', 'symbol' => 'โช' },
'PT' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'PW' => { 'code' => 'USD', 'symbol' => '$' },
'PY' => { 'code' => 'PYG', 'symbol' => 'Gs' },
'QA' => { 'code' => 'QAR', 'symbol' => '๏ทผ' },
'RE' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'RO' => { 'code' => 'RON', 'symbol' => 'lei' },
'RS' => { 'code' => 'RSD', 'symbol' => 'ะะธะฝ.' },
'RU' => { 'code' => 'RUB', 'symbol' => 'โฝ' },
'RW' => { 'code' => 'RWF', 'symbol' => 'Rโฃ' },
'SA' => { 'code' => 'SAR', 'symbol' => '๏ทผ' },
'SB' => { 'code' => 'SBD', 'symbol' => '$' },
'SC' => { 'code' => 'SCR', 'symbol' => 'โจ' },
'SD' => { 'code' => 'SDG', 'symbol' => 'ุฌ.ุณ.' },
'SE' => { 'code' => 'SEK', 'symbol' => 'kr' },
'SG' => { 'code' => 'SGD', 'symbol' => 'S$' },
'SH' => { 'code' => 'SHP', 'symbol' => 'ยฃ' },
'SI' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'SJ' => { 'code' => 'NOK', 'symbol' => 'kr' },
'SK' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'SL' => { 'code' => 'SLL', 'symbol' => 'Le' },
'SM' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'SN' => { 'code' => 'XOF', 'symbol' => 'CFA' },
'SO' => { 'code' => 'SOS', 'symbol' => 'S' },
'SR' => { 'code' => 'SRD', 'symbol' => '$' },
'SS' => { 'code' => 'SSP', 'symbol' => 'ยฃ' },
'ST' => { 'code' => 'STD', 'symbol' => 'Db' },
'SV' => { 'code' => 'USD', 'symbol' => '$' },
'SX' => { 'code' => 'ANG', 'symbol' => 'ฦ' },
'SY' => { 'code' => 'SYP', 'symbol' => 'ยฃ' },
'SZ' => { 'code' => 'SZL', 'symbol' => 'E' },
'TC' => { 'code' => 'USD', 'symbol' => '$' },
'TD' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
'TF' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'TG' => { 'code' => 'XOF', 'symbol' => 'CFA' },
'TH' => { 'code' => 'THB', 'symbol' => 'เธฟ' },
'TJ' => { 'code' => 'TJS', 'symbol' => 'SM' },
'TK' => { 'code' => 'NZD', 'symbol' => '$' },
'TL' => { 'code' => 'USD', 'symbol' => '$' },
'TM' => { 'code' => 'TMT', 'symbol' => 'T' },
'TN' => { 'code' => 'TND', 'symbol' => 'ุฏ.ุช' },
'TO' => { 'code' => 'TOP', 'symbol' => 'T$' },
'TR' => { 'code' => 'TRY', 'symbol' => 'โบ' },
'TT' => { 'code' => 'TTD', 'symbol' => 'TT$' },
'TV' => { 'code' => 'AUD', 'symbol' => '$' },
'TW' => { 'code' => 'TWD', 'symbol' => 'NT$' },
'TZ' => { 'code' => 'TZS', 'symbol' => 'TSh' },
'UA' => { 'code' => 'UAH', 'symbol' => 'โด' },
'UG' => { 'code' => 'UGX', 'symbol' => 'USh' },
'UM' => { 'code' => 'USD', 'symbol' => '$' },
'US' => { 'code' => 'USD', 'symbol' => '$' },
'UY' => { 'code' => 'UYU', 'symbol' => '$U' },
'UZ' => { 'code' => 'UZS', 'symbol' => 'ะปะฒ' },
'VA' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'VC' => { 'code' => 'XCD', 'symbol' => '$' },
'VE' => { 'code' => 'VEF', 'symbol' => 'Bs' },
'VG' => { 'code' => 'USD', 'symbol' => '$' },
'VI' => { 'code' => 'USD', 'symbol' => '$' },
'VN' => { 'code' => 'VND', 'symbol' => 'โซ' },
'VU' => { 'code' => 'VUV', 'symbol' => 'VT' },
'WF' => { 'code' => 'XPF', 'symbol' => 'โฃ' },
'WS' => { 'code' => 'WST', 'symbol' => 'WS$' },
'XK' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'YE' => { 'code' => 'YER', 'symbol' => '๏ทผ' },
'YT' => { 'code' => 'EUR', 'symbol' => 'โฌ' },
'ZA' => { 'code' => 'ZAR', 'symbol' => 'R' },
'ZM' => { 'code' => 'ZMK', 'symbol' => 'ZK' },
'ZW' => { 'code' => 'ZWL', 'symbol' => '$' }
);
my %default_continents = (
"BD" => { "code" => "AS", "name" => "Asia" },
"BE" => { "code" => "EU", "name" => "Europe" },
"BF" => { "code" => "AF", "name" => "Africa" },
"BG" => { "code" => "EU", "name" => "Europe" },
"BA" => { "code" => "EU", "name" => "Europe" },
"BB" => { "code" => "NA", "name" => "North America" },
"WF" => { "code" => "OC", "name" => "Oceania" },
"BL" => { "code" => "NA", "name" => "North America" },
"BM" => { "code" => "NA", "name" => "North America" },
"BN" => { "code" => "AS", "name" => "Asia" },
"BO" => { "code" => "SA", "name" => "South America" },
"BH" => { "code" => "AS", "name" => "Asia" },
"BI" => { "code" => "AF", "name" => "Africa" },
"BJ" => { "code" => "AF", "name" => "Africa" },
"BT" => { "code" => "AS", "name" => "Asia" },
"JM" => { "code" => "NA", "name" => "North America" },
"BV" => { "code" => "AN", "name" => "Antarctica" },
"BW" => { "code" => "AF", "name" => "Africa" },
"WS" => { "code" => "OC", "name" => "Oceania" },
"BQ" => { "code" => "NA", "name" => "North America" },
"BR" => { "code" => "SA", "name" => "South America" },
"BS" => { "code" => "NA", "name" => "North America" },
"JE" => { "code" => "EU", "name" => "Europe" },
"BY" => { "code" => "EU", "name" => "Europe" },
"BZ" => { "code" => "NA", "name" => "North America" },
"RU" => { "code" => "EU", "name" => "Europe" },
"RW" => { "code" => "AF", "name" => "Africa" },
"RS" => { "code" => "EU", "name" => "Europe" },
"TL" => { "code" => "OC", "name" => "Oceania" },
"RE" => { "code" => "AF", "name" => "Africa" },
"TM" => { "code" => "AS", "name" => "Asia" },
"TJ" => { "code" => "AS", "name" => "Asia" },
"RO" => { "code" => "EU", "name" => "Europe" },
"TK" => { "code" => "OC", "name" => "Oceania" },
"GW" => { "code" => "AF", "name" => "Africa" },
"GU" => { "code" => "OC", "name" => "Oceania" },
"GT" => { "code" => "NA", "name" => "North America" },
"GS" => { "code" => "AN", "name" => "Antarctica" },
"GR" => { "code" => "EU", "name" => "Europe" },
"GQ" => { "code" => "AF", "name" => "Africa" },
"GP" => { "code" => "NA", "name" => "North America" },
"JP" => { "code" => "AS", "name" => "Asia" },
"GY" => { "code" => "SA", "name" => "South America" },
"GG" => { "code" => "EU", "name" => "Europe" },
"GF" => { "code" => "SA", "name" => "South America" },
"GE" => { "code" => "AS", "name" => "Asia" },
"GD" => { "code" => "NA", "name" => "North America" },
"GB" => { "code" => "EU", "name" => "Europe" },
"GA" => { "code" => "AF", "name" => "Africa" },
"SV" => { "code" => "NA", "name" => "North America" },
"GN" => { "code" => "AF", "name" => "Africa" },
"GM" => { "code" => "AF", "name" => "Africa" },
"GL" => { "code" => "NA", "name" => "North America" },
"GI" => { "code" => "EU", "name" => "Europe" },
"GH" => { "code" => "AF", "name" => "Africa" },
"OM" => { "code" => "AS", "name" => "Asia" },
"TN" => { "code" => "AF", "name" => "Africa" },
"JO" => { "code" => "AS", "name" => "Asia" },
"HR" => { "code" => "EU", "name" => "Europe" },
"HT" => { "code" => "NA", "name" => "North America" },
"HU" => { "code" => "EU", "name" => "Europe" },
"HK" => { "code" => "AS", "name" => "Asia" },
"HN" => { "code" => "NA", "name" => "North America" },
"HM" => { "code" => "AN", "name" => "Antarctica" },
"VE" => { "code" => "SA", "name" => "South America" },
"PR" => { "code" => "NA", "name" => "North America" },
"PS" => { "code" => "AS", "name" => "Asia" },
"PW" => { "code" => "OC", "name" => "Oceania" },
"PT" => { "code" => "EU", "name" => "Europe" },
"SJ" => { "code" => "EU", "name" => "Europe" },
"PY" => { "code" => "SA", "name" => "South America" },
"IQ" => { "code" => "AS", "name" => "Asia" },
"PA" => { "code" => "NA", "name" => "North America" },
"PF" => { "code" => "OC", "name" => "Oceania" },
"PG" => { "code" => "OC", "name" => "Oceania" },
"PE" => { "code" => "SA", "name" => "South America" },
"PK" => { "code" => "AS", "name" => "Asia" },
"PH" => { "code" => "AS", "name" => "Asia" },
"PN" => { "code" => "OC", "name" => "Oceania" },
"PL" => { "code" => "EU", "name" => "Europe" },
"PM" => { "code" => "NA", "name" => "North America" },
"ZM" => { "code" => "AF", "name" => "Africa" },
"EH" => { "code" => "AF", "name" => "Africa" },
"EE" => { "code" => "EU", "name" => "Europe" },
"EG" => { "code" => "AF", "name" => "Africa" },
"ZA" => { "code" => "AF", "name" => "Africa" },
"EC" => { "code" => "SA", "name" => "South America" },
"IT" => { "code" => "EU", "name" => "Europe" },
"VN" => { "code" => "AS", "name" => "Asia" },
"SB" => { "code" => "OC", "name" => "Oceania" },
"ET" => { "code" => "AF", "name" => "Africa" },
"SO" => { "code" => "AF", "name" => "Africa" },
"ZW" => { "code" => "AF", "name" => "Africa" },
"SA" => { "code" => "AS", "name" => "Asia" },
"ES" => { "code" => "EU", "name" => "Europe" },
"ER" => { "code" => "AF", "name" => "Africa" },
"ME" => { "code" => "EU", "name" => "Europe" },
"MD" => { "code" => "EU", "name" => "Europe" },
"MG" => { "code" => "AF", "name" => "Africa" },
"MF" => { "code" => "NA", "name" => "North America" },
"MA" => { "code" => "AF", "name" => "Africa" },
"MC" => { "code" => "EU", "name" => "Europe" },
"UZ" => { "code" => "AS", "name" => "Asia" },
"MM" => { "code" => "AS", "name" => "Asia" },
"ML" => { "code" => "AF", "name" => "Africa" },
"MO" => { "code" => "AS", "name" => "Asia" },
"MN" => { "code" => "AS", "name" => "Asia" },
"MH" => { "code" => "OC", "name" => "Oceania" },
"MK" => { "code" => "EU", "name" => "Europe" },
"MU" => { "code" => "AF", "name" => "Africa" },
"MT" => { "code" => "EU", "name" => "Europe" },
"MW" => { "code" => "AF", "name" => "Africa" },
"MV" => { "code" => "AS", "name" => "Asia" },
"MQ" => { "code" => "NA", "name" => "North America" },
"MP" => { "code" => "OC", "name" => "Oceania" },
"MS" => { "code" => "NA", "name" => "North America" },
"MR" => { "code" => "AF", "name" => "Africa" },
"IM" => { "code" => "EU", "name" => "Europe" },
"UG" => { "code" => "AF", "name" => "Africa" },
"TZ" => { "code" => "AF", "name" => "Africa" },
"MY" => { "code" => "AS", "name" => "Asia" },
"MX" => { "code" => "NA", "name" => "North America" },
"IL" => { "code" => "AS", "name" => "Asia" },
"FR" => { "code" => "EU", "name" => "Europe" },
"IO" => { "code" => "AS", "name" => "Asia" },
"SH" => { "code" => "AF", "name" => "Africa" },
"FI" => { "code" => "EU", "name" => "Europe" },
"FJ" => { "code" => "OC", "name" => "Oceania" },
"FK" => { "code" => "SA", "name" => "South America" },
"FM" => { "code" => "OC", "name" => "Oceania" },
"FO" => { "code" => "EU", "name" => "Europe" },
"NI" => { "code" => "NA", "name" => "North America" },
"NL" => { "code" => "EU", "name" => "Europe" },
"NO" => { "code" => "EU", "name" => "Europe" },
"NA" => { "code" => "AF", "name" => "Africa" },
"VU" => { "code" => "OC", "name" => "Oceania" },
"NC" => { "code" => "OC", "name" => "Oceania" },
"NE" => { "code" => "AF", "name" => "Africa" },
"NF" => { "code" => "OC", "name" => "Oceania" },
"NG" => { "code" => "AF", "name" => "Africa" },
"NZ" => { "code" => "OC", "name" => "Oceania" },
"NP" => { "code" => "AS", "name" => "Asia" },
"NR" => { "code" => "OC", "name" => "Oceania" },
"NU" => { "code" => "OC", "name" => "Oceania" },
"CK" => { "code" => "OC", "name" => "Oceania" },
"XK" => { "code" => "EU", "name" => "Europe" },
"CI" => { "code" => "AF", "name" => "Africa" },
"CH" => { "code" => "EU", "name" => "Europe" },
"CO" => { "code" => "SA", "name" => "South America" },
"CN" => { "code" => "AS", "name" => "Asia" },
"CM" => { "code" => "AF", "name" => "Africa" },
"CL" => { "code" => "SA", "name" => "South America" },
"CC" => { "code" => "AS", "name" => "Asia" },
"CA" => { "code" => "NA", "name" => "North America" },
"CG" => { "code" => "AF", "name" => "Africa" },
"CF" => { "code" => "AF", "name" => "Africa" },
"CD" => { "code" => "AF", "name" => "Africa" },
"CZ" => { "code" => "EU", "name" => "Europe" },
"CY" => { "code" => "EU", "name" => "Europe" },
"CX" => { "code" => "AS", "name" => "Asia" },
"CR" => { "code" => "NA", "name" => "North America" },
"CW" => { "code" => "NA", "name" => "North America" },
"CV" => { "code" => "AF", "name" => "Africa" },
"CU" => { "code" => "NA", "name" => "North America" },
"SZ" => { "code" => "AF", "name" => "Africa" },
"SY" => { "code" => "AS", "name" => "Asia" },
"SX" => { "code" => "NA", "name" => "North America" },
"KG" => { "code" => "AS", "name" => "Asia" },
"KE" => { "code" => "AF", "name" => "Africa" },
"SS" => { "code" => "AF", "name" => "Africa" },
"SR" => { "code" => "SA", "name" => "South America" },
"KI" => { "code" => "OC", "name" => "Oceania" },
"KH" => { "code" => "AS", "name" => "Asia" },
"KN" => { "code" => "NA", "name" => "North America" },
"KM" => { "code" => "AF", "name" => "Africa" },
"ST" => { "code" => "AF", "name" => "Africa" },
"SK" => { "code" => "EU", "name" => "Europe" },
"KR" => { "code" => "AS", "name" => "Asia" },
"SI" => { "code" => "EU", "name" => "Europe" },
"KP" => { "code" => "AS", "name" => "Asia" },
"KW" => { "code" => "AS", "name" => "Asia" },
"SN" => { "code" => "AF", "name" => "Africa" },
"SM" => { "code" => "EU", "name" => "Europe" },
"SL" => { "code" => "AF", "name" => "Africa" },
"SC" => { "code" => "AF", "name" => "Africa" },
"KZ" => { "code" => "AS", "name" => "Asia" },
"KY" => { "code" => "NA", "name" => "North America" },
"SG" => { "code" => "AS", "name" => "Asia" },
"SE" => { "code" => "EU", "name" => "Europe" },
"SD" => { "code" => "AF", "name" => "Africa" },
"DO" => { "code" => "NA", "name" => "North America" },
"DM" => { "code" => "NA", "name" => "North America" },
"DJ" => { "code" => "AF", "name" => "Africa" },
"DK" => { "code" => "EU", "name" => "Europe" },
"VG" => { "code" => "NA", "name" => "North America" },
"DE" => { "code" => "EU", "name" => "Europe" },
"YE" => { "code" => "AS", "name" => "Asia" },
"DZ" => { "code" => "AF", "name" => "Africa" },
"US" => { "code" => "NA", "name" => "North America" },
"UY" => { "code" => "SA", "name" => "South America" },
"YT" => { "code" => "AF", "name" => "Africa" },
"UM" => { "code" => "OC", "name" => "Oceania" },
"LB" => { "code" => "AS", "name" => "Asia" },
"LC" => { "code" => "NA", "name" => "North America" },
"LA" => { "code" => "AS", "name" => "Asia" },
"TV" => { "code" => "OC", "name" => "Oceania" },
"TW" => { "code" => "AS", "name" => "Asia" },
"TT" => { "code" => "NA", "name" => "North America" },
"TR" => { "code" => "AS", "name" => "Asia" },
"LK" => { "code" => "AS", "name" => "Asia" },
"LI" => { "code" => "EU", "name" => "Europe" },
"LV" => { "code" => "EU", "name" => "Europe" },
"TO" => { "code" => "OC", "name" => "Oceania" },
"LT" => { "code" => "EU", "name" => "Europe" },
"LU" => { "code" => "EU", "name" => "Europe" },
"LR" => { "code" => "AF", "name" => "Africa" },
"LS" => { "code" => "AF", "name" => "Africa" },
"TH" => { "code" => "AS", "name" => "Asia" },
"TF" => { "code" => "AN", "name" => "Antarctica" },
"TG" => { "code" => "AF", "name" => "Africa" },
"TD" => { "code" => "AF", "name" => "Africa" },
"TC" => { "code" => "NA", "name" => "North America" },
"LY" => { "code" => "AF", "name" => "Africa" },
"VA" => { "code" => "EU", "name" => "Europe" },
"VC" => { "code" => "NA", "name" => "North America" },
"AE" => { "code" => "AS", "name" => "Asia" },
"AD" => { "code" => "EU", "name" => "Europe" },
"AG" => { "code" => "NA", "name" => "North America" },
"AF" => { "code" => "AS", "name" => "Asia" },
"AI" => { "code" => "NA", "name" => "North America" },
"VI" => { "code" => "NA", "name" => "North America" },
"IS" => { "code" => "EU", "name" => "Europe" },
"IR" => { "code" => "AS", "name" => "Asia" },
"AM" => { "code" => "AS", "name" => "Asia" },
"AL" => { "code" => "EU", "name" => "Europe" },
"AO" => { "code" => "AF", "name" => "Africa" },
"AQ" => { "code" => "AN", "name" => "Antarctica" },
"AS" => { "code" => "OC", "name" => "Oceania" },
"AR" => { "code" => "SA", "name" => "South America" },
"AU" => { "code" => "OC", "name" => "Oceania" },
"AT" => { "code" => "EU", "name" => "Europe" },
"AW" => { "code" => "NA", "name" => "North America" },
"IN" => { "code" => "AS", "name" => "Asia" },
"AX" => { "code" => "EU", "name" => "Europe" },
"AZ" => { "code" => "AS", "name" => "Asia" },
"IE" => { "code" => "EU", "name" => "Europe" },
"ID" => { "code" => "AS", "name" => "Asia" },
"UA" => { "code" => "EU", "name" => "Europe" },
"QA" => { "code" => "AS", "name" => "Asia" },
"MZ" => { "code" => "AF", "name" => "Africa" }
);
sub new {
my ( $pkg, $token, %options ) = @_;
my $self = {};
$token = defined $token ? $token : '';
$self->{base_url} = $base_url;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->ssl_opts( 'verify_hostname' => 0 );
$self->{ua}->default_headers(
HTTP::Headers->new(
Accept => 'application/json',
Authorization => 'Bearer ' . $token
)
);
$self->{ua}->agent("IPinfoClient/Perl/$VERSION");
my $timeout =
defined $options{timeout} ? $options{timeout} : DEFAULT_TIMEOUT;
$self->{ua}->timeout($timeout);
$self->{message} = '';
bless $self, $pkg;
my $countries =
exists $options{countries} ? $options{countries} : \%default_countries;
my $eu_countries =
exists $options{eu_countries}
? $options{eu_countries}
: \@default_eu_countries;
my $countries_flags =
exists $options{countries_flags}
? $options{countries_flags}
: \%default_countries_flags;
my $countries_currencies =
exists $options{countries_currencies}
? $options{countries_currencies}
: \%default_countries_currencies;
my $continents =
exists $options{continents}
? $options{continents}
: \%default_continents;
$self->{countries} = $countries;
$self->{eu_countries} = $eu_countries;
$self->{countries_flags} = $countries_flags;
$self->{countries_currencies} = $countries_currencies;
$self->{continents} = $continents;
$self->{cache} = $self->_build_cache(%options);
return $self;
}
sub info {
my ( $self, $ip ) = @_;
return $self->_get_info( $ip, );
}
sub _get_info {
my ( $self, $ip ) = @_;
$ip = defined $ip ? $ip : '';
if ( $ip ne '' ) {
my $validated_ip = Net::CIDR::cidrvalidate($ip);
if ( !defined $validated_ip ) {
$self->{message} = 'Invalid IP address';
return undef;
}
}
my ( $info, $message ) = $self->_lookup_info( $ip );
$self->{message} = $message;
return $info if eval { $info->isa('Geo::DetailsLite') };
return defined $info ? Geo::DetailsLite->new( $info ) : undef;
}
sub _lookup_info {
my ( $self, $ip ) = @_;
# checking bogon IP and returning response locally.
if ( $ip ne '' ) {
if ( _is_bogon($ip) ) {
my $details = {};
$details->{ip} = $ip;
$details->{bogon} = "True";
return ( $details, '' );
}
}
my $cached_info = $self->_lookup_info_from_cache($ip);
if ( defined $cached_info ) {
return ( $cached_info, '' );
}
my ( $source_info, $message ) = $self->_lookup_info_from_source($ip);
if ( not defined $source_info ) {
return ( $source_info, $message );
}
if ( ref($source_info) eq '' ) {
return ( $source_info, $message );
}
my $country_code = $source_info->{country_code};
if ( defined $country_code ) {
$source_info->{country_name} = $self->{countries}->{country_code};
$source_info->{country_flag} = $self->{countries_flags}->{country_code};
$source_info->{country_flag_url} =
$country_flag_url . $country_code . ".svg";
$source_info->{country_currency} =
$self->{countries_currencies}->{country_code};
$source_info->{continent} = $self->{continents}->{country_code};
if ( grep { $_ eq $country_code } @{ $self->{eu_countries} } ) {
$source_info->{is_eu} = "True";
}
else {
$source_info->{is_eu} = undef;
}
}
if ( defined $source_info->{'loc'} ) {
my ( $lat, $lon ) = split /,/, $source_info->{loc};
$source_info->{latitude} = $lat;
$source_info->{longitude} = $lon;
}
$source_info->{meta} = { time => time(), from_cache => 0 };
$self->{cache}->set( $ip, $source_info );
return ( $source_info, $message );
}
sub _lookup_info_from_cache {
my ( $self, $cache_key ) = @_;
my $cached_info = $self->{cache}->get($cache_key);
if ( defined $cached_info ) {
my $timedelta = time() - $cached_info->{meta}->{time};
if ( $timedelta <= $cache_ttl || $custom_cache == 1 ) {
$cached_info->{meta}->{from_cache} = 1;
return $cached_info;
}
}
return;
}
sub _lookup_info_from_source {
my ( $self, $ip ) = @_;
my $url = '';
if ( $ip ) {
$url = $self->{base_url} . $ip;
} else {
$url = $self->{base_url} . "me";
}
my $response = $self->{ua}->get($url);
if ( $response->is_success ) {
my $content_type = $response->header('Content-Type') || '';
my $info;
if ( $content_type =~ m{application/json}i ) {
eval { $info = from_json( $response->decoded_content ); };
if ($@) {
return ( undef, 'Error parsing JSON response.' );
}
}
else {
$info = $response->decoded_content;
chomp($info);
}
return ( $info, '' );
}
if ( $response->code == HTTP_TOO_MANY_REQUEST ) {
return ( undef, 'Your monthly request quota has been exceeded.' );
}
return ( undef, $response->status_line );
}
sub _build_cache {
my ( $pkg, %options ) = @_;
if ( defined $options{cache} ) {
$custom_cache = 1;
return $options{cache};
}
$cache_ttl = DEFAULT_CACHE_TTL;
if ( defined $options{cache_ttl} ) {
$cache_ttl = $options{cache_ttl};
}
return Cache::LRU->new(
size => defined $options{cache_max_size}
? $options{cache_max_size}
: DEFAULT_CACHE_MAX_SIZE
);
}
# Lists of bogon CIDRs.
my @ip4_bogon_networks = (
"0.0.0.0/8", "10.0.0.0/8",
"100.64.0.0/10", "127.0.0.0/8",
"169.254.0.0/16", "172.16.0.0/12",
"192.0.0.0/24", "192.0.2.0/24",
"192.168.0.0/16", "198.18.0.0/15",
"198.51.100.0/24", "203.0.113.0/24",
"224.0.0.0/4", "240.0.0.0/4",
"255.255.255.255/32"
);
my @ip6_bogon_networks = (
"0:0:0:0:0:0:0:0/128", "0:0:0:0:0:0:0:1/128",
"0:0:0:0:0:ffff:0:0/96", "0:0:0:0:0:0:0:0/96",
"100::/64", "2001:10::/28",
"2001:db8::/32", "fc00::/7",
"fe80::/10", "fec0::/10",
"ff00::/8", "2002::/24",
"2002:a00::/24", "2002:7f00::/24",
"2002:a9fe::/32", "2002:ac10::/28",
"2002:c000::/40", "2002:c000:200::/40",
"2002:c0a8::/32", "2002:c612::/31",
"2002:c633:6400::/40", "2002:cb00:7100::/40",
"2002:e000::/20", "2002:f000::/20",
"2002:ffff:ffff::/48", "2001::/40",
"2001:0:a00::/40", "2001:0:7f00::/40",
"2001:0:a9fe::/48", "2001:0:ac10::/44",
"2001:0:c000::/56", "2001:0:c000:200::/56",
"2001:0:c0a8::/48", "2001:0:c612::/47",
"2001:0:c633:6400::/56", "2001:0:cb00:7100::/56",
"2001:0:e000::/36", "2001:0:f000::/36",
"2001:0:ffff:ffff::/64"
);
# Check if an IP address is a bogon.
sub _is_bogon {
my $ip = shift;
my $ip_is_bogon = 0;
if ( $ip =~ /:/ ) { # IPv6 address
my $ip6_bogon_cidr_set = Net::CIDR::Set->new();
$ip6_bogon_cidr_set->add($_) foreach (@ip6_bogon_networks);
$ip_is_bogon = $ip6_bogon_cidr_set->contains($ip);
}
else { # IPv4 address
my $ip4_bogon_cidr_set = Net::CIDR::Set->new();
$ip4_bogon_cidr_set->add($_) foreach (@ip4_bogon_networks);
$ip_is_bogon = $ip4_bogon_cidr_set->contains($ip);
}
return $ip_is_bogon;
}