IPinfo 1.0.1 → 2.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/cd_rubygems.yaml +37 -0
- data/.github/workflows/unittest.yaml +35 -0
- data/.rubocop.yml +6 -0
- data/.ruby-version +1 -1
- data/README.md +88 -20
- data/ipinfo.gemspec +8 -10
- data/lib/ipinfo/adapter.rb +15 -5
- data/lib/ipinfo/countriesData.rb +1045 -0
- data/lib/ipinfo/ipAddressMatcher.rb +92 -0
- data/lib/ipinfo/version.rb +1 -1
- data/lib/ipinfo.rb +123 -32
- metadata +92 -6
- data/.travis.yml +0 -11
- data/lib/ipinfo/countries.json +0 -1
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'resolv'
|
4
|
+
require 'ipaddr'
|
5
|
+
|
6
|
+
class IPinfo::IpAddressMatcher
|
7
|
+
attr_accessor :required_address
|
8
|
+
|
9
|
+
@@bogon_list = [
|
10
|
+
"0.0.0.0/8",
|
11
|
+
"10.0.0.0/8",
|
12
|
+
"100.64.0.0/10",
|
13
|
+
"127.0.0.0/8",
|
14
|
+
"169.254.0.0/16",
|
15
|
+
"172.16.0.0/12",
|
16
|
+
"192.0.0.0/24",
|
17
|
+
"192.0.2.0/24",
|
18
|
+
"192.168.0.0/16",
|
19
|
+
"198.18.0.0/15",
|
20
|
+
"198.51.100.0/24",
|
21
|
+
"203.0.113.0/24",
|
22
|
+
"224.0.0.0/4",
|
23
|
+
"240.0.0.0/4",
|
24
|
+
"255.255.255.255/32",
|
25
|
+
"::/128",
|
26
|
+
"::1/128",
|
27
|
+
"::ffff:0:0/96",
|
28
|
+
"::/96",
|
29
|
+
"100::/64",
|
30
|
+
"2001:10::/28",
|
31
|
+
"2001:db8::/32",
|
32
|
+
"fc00::/7",
|
33
|
+
"fe80::/10",
|
34
|
+
"fec0::/10",
|
35
|
+
"ff00::/8",
|
36
|
+
"2002::/24",
|
37
|
+
"2002:a00::/24",
|
38
|
+
"2002:7f00::/24",
|
39
|
+
"2002:a9fe::/32",
|
40
|
+
"2002:ac10::/28",
|
41
|
+
"2002:c000::/40",
|
42
|
+
"2002:c000:200::/40",
|
43
|
+
"2002:c0a8::/32",
|
44
|
+
"2002:c612::/31",
|
45
|
+
"2002:c633:6400::/40",
|
46
|
+
"2002:cb00:7100::/40",
|
47
|
+
"2002:e000::/20",
|
48
|
+
"2002:f000::/20",
|
49
|
+
"2002:ffff:ffff::/48",
|
50
|
+
"2001::/40",
|
51
|
+
"2001:0:a00::/40",
|
52
|
+
"2001:0:7f00::/40",
|
53
|
+
"2001:0:a9fe::/48",
|
54
|
+
"2001:0:ac10::/44",
|
55
|
+
"2001:0:c000::/56",
|
56
|
+
"2001:0:c000:200::/56",
|
57
|
+
"2001:0:c0a8::/48",
|
58
|
+
"2001:0:c612::/47",
|
59
|
+
"2001:0:c633:6400::/56",
|
60
|
+
"2001:0:cb00:7100::/56",
|
61
|
+
"2001:0:e000::/36",
|
62
|
+
"2001:0:f000::/36",
|
63
|
+
"2001:0:ffff:ffff::/64"
|
64
|
+
]
|
65
|
+
|
66
|
+
def initialize(ip)
|
67
|
+
if !ip.index('/').nil? and ip.index('/') > 0
|
68
|
+
addr_mask = ip.split('/')
|
69
|
+
ip = addr_mask[0]
|
70
|
+
end
|
71
|
+
|
72
|
+
@required_address = parseAddress(ip)
|
73
|
+
end
|
74
|
+
|
75
|
+
def parseAddress(ip)
|
76
|
+
begin
|
77
|
+
Resolv.getaddress ip
|
78
|
+
rescue Resolv::ResolvError => e
|
79
|
+
pp e
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def matches
|
84
|
+
for bogon in @@bogon_list do
|
85
|
+
if bogon.include? required_address
|
86
|
+
return true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
false
|
91
|
+
end
|
92
|
+
end
|
data/lib/ipinfo/version.rb
CHANGED
data/lib/ipinfo.rb
CHANGED
@@ -8,14 +8,18 @@ require 'ipinfo/errors'
|
|
8
8
|
require 'ipinfo/response'
|
9
9
|
require 'ipinfo/version'
|
10
10
|
require 'json'
|
11
|
+
require_relative 'ipinfo/ipAddressMatcher'
|
12
|
+
require_relative 'ipinfo/countriesData'
|
11
13
|
|
12
14
|
module IPinfo
|
15
|
+
include CountriesData
|
13
16
|
DEFAULT_CACHE_MAXSIZE = 4096
|
14
17
|
DEFAULT_CACHE_TTL = 60 * 60 * 24
|
15
|
-
DEFAULT_COUNTRY_FILE = File.join(File.dirname(__FILE__),
|
16
|
-
'ipinfo/countries.json')
|
17
18
|
RATE_LIMIT_MESSAGE = 'To increase your limits, please review our ' \
|
18
19
|
'paid plans at https://ipinfo.io/pricing'
|
20
|
+
# Base URL to get country flag image link.
|
21
|
+
# "PK" -> "https://cdn.ipinfo.io/static/images/countries-flags/PK.svg"
|
22
|
+
COUNTRY_FLAGS_URL = "https://cdn.ipinfo.io/static/images/countries-flags/"
|
19
23
|
|
20
24
|
class << self
|
21
25
|
def create(access_token = nil, settings = {})
|
@@ -30,43 +34,102 @@ class IPinfo::IPinfo
|
|
30
34
|
|
31
35
|
def initialize(access_token = nil, settings = {})
|
32
36
|
@access_token = access_token
|
33
|
-
|
37
|
+
prepare_http_client(settings.fetch('http_client', nil))
|
34
38
|
|
35
39
|
maxsize = settings.fetch('maxsize', DEFAULT_CACHE_MAXSIZE)
|
36
40
|
ttl = settings.fetch('ttl', DEFAULT_CACHE_TTL)
|
37
41
|
@cache = settings.fetch('cache', DefaultCache.new(ttl, maxsize))
|
38
|
-
@countries =
|
39
|
-
|
42
|
+
@countries = settings.fetch('countries', DEFAULT_COUNTRY_LIST)
|
43
|
+
@eu_countries = settings.fetch('eu_countries', DEFAULT_EU_COUNTRIES_LIST)
|
44
|
+
@countries_flags = settings.fetch('countries_flags', DEFAULT_COUNTRIES_FLAG_LIST)
|
45
|
+
@countries_currencies = settings.fetch('countries_currencies', DEFAULT_COUNTRIES_CURRENCIES_LIST)
|
46
|
+
@continents = settings.fetch('continents', DEFAULT_CONTINENT_LIST)
|
40
47
|
end
|
41
48
|
|
42
49
|
def details(ip_address = nil)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
50
|
+
details_base(ip_address, :v4)
|
51
|
+
end
|
52
|
+
|
53
|
+
def details_v6(ip_address = nil)
|
54
|
+
details_base(ip_address, :v6)
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_map_url(ips)
|
58
|
+
if !ips.kind_of?(Array)
|
59
|
+
return JSON.generate({:error => 'Invalid input. Array required!'})
|
60
|
+
end
|
61
|
+
if ips.length > 500000
|
62
|
+
return JSON.generate({:error => 'No more than 500,000 ips allowed!'})
|
47
63
|
end
|
48
64
|
|
49
|
-
|
50
|
-
|
51
|
-
|
65
|
+
json_ips = JSON.generate({:ips => ips})
|
66
|
+
res = @httpc.post('/tools/map', json_ips)
|
67
|
+
|
68
|
+
obj = JSON.parse(res.body)
|
69
|
+
obj['reportUrl']
|
70
|
+
end
|
71
|
+
|
72
|
+
def batch_requests(url_array, api_token)
|
73
|
+
result = Hash.new
|
74
|
+
lookup_ips = []
|
75
|
+
|
76
|
+
url_array.each { |url|
|
77
|
+
ip = @cache.get(cache_key(url))
|
78
|
+
|
79
|
+
unless ip.nil?
|
80
|
+
result.store(url, ip)
|
81
|
+
else
|
82
|
+
lookup_ips << url
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
if lookup_ips.empty?
|
87
|
+
return result
|
52
88
|
end
|
53
89
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
90
|
+
begin
|
91
|
+
lookup_ips.each_slice(1000){ |ips|
|
92
|
+
json_arr = JSON.generate(lookup_ips)
|
93
|
+
res = @httpc.post("/batch?token=#{api_token}", json_arr, 5)
|
94
|
+
|
95
|
+
raise StandardError, "Request Quota Exceeded" if res.status == 429
|
96
|
+
|
97
|
+
data = JSON.parse(res.body)
|
98
|
+
data.each { |key, val|
|
99
|
+
@cache.set(cache_key(key), val)
|
100
|
+
}
|
101
|
+
|
102
|
+
result.merge!(data)
|
103
|
+
}
|
104
|
+
|
105
|
+
rescue StandardError => e
|
106
|
+
return e.message
|
58
107
|
end
|
59
108
|
|
60
|
-
|
109
|
+
result
|
61
110
|
end
|
62
111
|
|
63
112
|
protected
|
64
113
|
|
65
|
-
def
|
66
|
-
|
114
|
+
def prepare_http_client(httpc = nil)
|
115
|
+
@httpc = Adapter.new(access_token, httpc || :net_http)
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def request_details(ip_address = nil, host_type)
|
121
|
+
if isBogon(ip_address)
|
122
|
+
details[:ip] = ip_address
|
123
|
+
details[:bogon] = true
|
124
|
+
details[:ip_address] = IPAddr.new(ip_address)
|
125
|
+
|
126
|
+
return details
|
127
|
+
end
|
128
|
+
|
129
|
+
res = @cache.get(cache_key(ip_address))
|
67
130
|
return res unless res.nil?
|
68
131
|
|
69
|
-
response = @httpc.get(escape_path(ip_address))
|
132
|
+
response = @httpc.get(escape_path(ip_address), host_type)
|
70
133
|
|
71
134
|
if response.status.eql?(429)
|
72
135
|
raise RateLimitError,
|
@@ -74,26 +137,54 @@ class IPinfo::IPinfo
|
|
74
137
|
end
|
75
138
|
|
76
139
|
details = JSON.parse(response.body, symbolize_names: true)
|
77
|
-
@cache.set(ip_address, details)
|
140
|
+
@cache.set(cache_key(ip_address), details)
|
78
141
|
details
|
79
142
|
end
|
80
143
|
|
81
|
-
def
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
144
|
+
def details_base(ip_address, host_type)
|
145
|
+
details = request_details(ip_address, host_type)
|
146
|
+
if details.key? :country
|
147
|
+
details[:country_name] =
|
148
|
+
@countries.fetch(details.fetch(:country), nil)
|
149
|
+
details[:is_eu] =
|
150
|
+
@eu_countries.include?(details.fetch(:country))
|
151
|
+
details[:country_flag] =
|
152
|
+
@countries_flags.fetch(details.fetch(:country), nil)
|
153
|
+
details[:country_currency] =
|
154
|
+
@countries_currencies.fetch(details.fetch(:country), nil)
|
155
|
+
details[:continent] =
|
156
|
+
@continents.fetch(details.fetch(:country), nil)
|
157
|
+
details[:country_flag_url] = COUNTRY_FLAGS_URL + details.fetch(:country) + ".svg"
|
158
|
+
end
|
88
159
|
|
89
|
-
|
90
|
-
|
91
|
-
|
160
|
+
if details.key? :ip
|
161
|
+
details[:ip_address] =
|
162
|
+
IPAddr.new(details.fetch(:ip))
|
163
|
+
end
|
164
|
+
|
165
|
+
if details.key? :loc
|
166
|
+
loc = details.fetch(:loc).split(',')
|
167
|
+
details[:latitude] = loc[0]
|
168
|
+
details[:longitude] = loc[1]
|
169
|
+
end
|
170
|
+
|
171
|
+
Response.new(details)
|
92
172
|
end
|
93
173
|
|
94
|
-
|
174
|
+
def isBogon(ip)
|
175
|
+
if ip.nil?
|
176
|
+
return false
|
177
|
+
end
|
178
|
+
|
179
|
+
matcher_object = IpAddressMatcher.new(ip)
|
180
|
+
matcher_object.matches
|
181
|
+
end
|
95
182
|
|
96
183
|
def escape_path(ip)
|
97
184
|
ip ? "/#{CGI.escape(ip)}" : '/'
|
98
185
|
end
|
186
|
+
|
187
|
+
def cache_key(ip)
|
188
|
+
"1:#{ip}"
|
189
|
+
end
|
99
190
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: IPinfo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stanislav K, James Timmins
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-09-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
@@ -17,14 +17,98 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '2.0'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '2.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: async-http-faraday
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: faraday-net_http_persistent
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '2.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '2.0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: faraday-typhoeus
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
24
59
|
requirements:
|
25
60
|
- - "~>"
|
26
61
|
- !ruby/object:Gem::Version
|
27
62
|
version: '1.0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: faraday-patron
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '2.0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '2.0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: faraday-httpclient
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '2.0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '2.0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: faraday-excon
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '2.1'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '2.1'
|
28
112
|
- !ruby/object:Gem::Dependency
|
29
113
|
name: json
|
30
114
|
requirement: !ruby/object:Gem::Requirement
|
@@ -62,10 +146,11 @@ extensions: []
|
|
62
146
|
extra_rdoc_files: []
|
63
147
|
files:
|
64
148
|
- ".editorconfig"
|
149
|
+
- ".github/workflows/cd_rubygems.yaml"
|
150
|
+
- ".github/workflows/unittest.yaml"
|
65
151
|
- ".gitignore"
|
66
152
|
- ".rubocop.yml"
|
67
153
|
- ".ruby-version"
|
68
|
-
- ".travis.yml"
|
69
154
|
- Gemfile
|
70
155
|
- LICENSE
|
71
156
|
- README.md
|
@@ -77,8 +162,9 @@ files:
|
|
77
162
|
- lib/ipinfo/adapter.rb
|
78
163
|
- lib/ipinfo/cache/cache_interface.rb
|
79
164
|
- lib/ipinfo/cache/default_cache.rb
|
80
|
-
- lib/ipinfo/
|
165
|
+
- lib/ipinfo/countriesData.rb
|
81
166
|
- lib/ipinfo/errors.rb
|
167
|
+
- lib/ipinfo/ipAddressMatcher.rb
|
82
168
|
- lib/ipinfo/mod.rb
|
83
169
|
- lib/ipinfo/response.rb
|
84
170
|
- lib/ipinfo/version.rb
|
@@ -100,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
186
|
- !ruby/object:Gem::Version
|
101
187
|
version: '0'
|
102
188
|
requirements: []
|
103
|
-
rubygems_version: 3.
|
189
|
+
rubygems_version: 3.4.10
|
104
190
|
signing_key:
|
105
191
|
specification_version: 4
|
106
192
|
summary: This is a ruby wrapper for http://ipinfo.io.
|
data/.travis.yml
DELETED
data/lib/ipinfo/countries.json
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
{"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"}
|