validators 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +3 -0
  4. data/README.md +4 -4
  5. data/bin/sync-disposable-hostnames +120 -110
  6. data/bin/sync-tld +2 -2
  7. data/data/country_tlds.txt +235 -0
  8. data/data/disposable_domains.txt +110996 -0
  9. data/data/disposable_emails.txt +38 -0
  10. data/data/reserved_subdomains.txt +2830 -0
  11. data/data/tld.txt +1508 -0
  12. data/lib/validators.rb +1 -0
  13. data/lib/validators/constants.rb +1 -1
  14. data/lib/validators/disposable_emails.rb +19 -0
  15. data/lib/validators/disposable_hostnames.rb +2 -2
  16. data/lib/validators/locale/en.yml +2 -1
  17. data/lib/validators/locale/pt-BR.yml +1 -0
  18. data/lib/validators/reserved_subdomains.rb +2 -4
  19. data/lib/validators/tld.rb +2 -2
  20. data/lib/validators/validates_email_format_of.rb +19 -4
  21. data/lib/validators/version.rb +2 -2
  22. data/test/test_helper.rb +17 -1
  23. data/test/validators/disposable_email_test.rb +18 -5
  24. data/test/validators/validates_email_format_of_test.rb +4 -2
  25. data/validators.gemspec +3 -0
  26. metadata +53 -43
  27. data/data/country_tlds.json +0 -237
  28. data/data/disposable.json +0 -128
  29. data/data/disposable/10minutemail.json +0 -3
  30. data/data/disposable/1secmail.json +0 -5
  31. data/data/disposable/FGRibreau_mailchecker.json +0 -33602
  32. data/data/disposable/andreis_disposable_email_domains.json +0 -48320
  33. data/data/disposable/clipmails.json +0 -27
  34. data/data/disposable/cs.json +0 -15
  35. data/data/disposable/emailfake.json +0 -128
  36. data/data/disposable/fake_email_generator.json +0 -11
  37. data/data/disposable/fnando_dafe542cac13f831bbf5521a55248116.json +0 -20
  38. data/data/disposable/gmailnator.json +0 -3
  39. data/data/disposable/guerrillamail.json +0 -13
  40. data/data/disposable/ically.json +0 -28
  41. data/data/disposable/itemp.json +0 -4
  42. data/data/disposable/ivolo_disposable_email_domains.json +0 -48177
  43. data/data/disposable/jespernissen_disposable_maildomain_list.json +0 -2372
  44. data/data/disposable/maxmalysh_disposable_emails.json +0 -21372
  45. data/data/disposable/moakt.json +0 -12
  46. data/data/disposable/receivemail.json +0 -9
  47. data/data/disposable/sneakykiwi_LeagueCreatorPublic.json +0 -2364
  48. data/data/disposable/tempemail.json +0 -7
  49. data/data/disposable/tempemails.json +0 -10
  50. data/data/disposable/tempmail.json +0 -31
  51. data/data/disposable/tempmail_io.json +0 -8
  52. data/data/disposable/tempmailaddress.json +0 -4
  53. data/data/disposable/tempomail.json +0 -3
  54. data/data/disposable/tempr.json +0 -97
  55. data/data/disposable/tmail.json +0 -3
  56. data/data/disposable/wesbos_burner_email_providers.json +0 -4711
  57. data/data/disposable/willwhite_freemail.json +0 -352
  58. data/data/disposable/yepmail.json +0 -12
  59. data/data/disposable_emails.json +0 -3
  60. data/data/disposable_patterns.json +0 -3
  61. data/data/disposable_raw.json +0 -128
  62. data/data/reserved_subdomains.json +0 -2838
  63. data/data/sld.json +0 -5564
  64. data/data/tld.json +0 -1518
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e2c068d28878c6cd22e62c6842ed48300e66edf545693b97e08fb6e8ed01d53
4
- data.tar.gz: 95b90e034d61f65ceb1a1538f906266f882b35d2ca8438b5186ea5caba2f9b6c
3
+ metadata.gz: 862ad7c2a49bcc38d917336b109047a330c6de6c6da16d83ea7953ddefe70096
4
+ data.tar.gz: fb5e0b9db3ec0fdbc064eb6467cb36fab3d0f4bab6f29a5c8439c8d5edeb46fd
5
5
  SHA512:
6
- metadata.gz: d85e1a89b5b106d7b1e69809f7a314f001ada7e68416a74d8a08a3875bacd55c6634d02792ac53fe418c63a41253ea3787f2ff5bc257309c01c1cdbb07c07bcf
7
- data.tar.gz: a12a716b4960cf40fd37f23ff0d049fe2c5efca3dc310c65c96e24d0a5c714c54c7426cb8170ce7ba45023d6367714833e61462db5c97bc6751774b946194d3e
6
+ metadata.gz: b8842cf3ee2161ef2fe18c384efdb5ff6407c135560a95b91689d29b8ff65d87272c599288de55a6422dfaa6ff4be3b0b00fbd7ea8a8472e47bc08acfb973a85
7
+ data.tar.gz: 7a6fe664d6ef7a33735b8bf41cc198f8509a5712f788a2db93d6f2c4b39678ad6965eea1557fb6e43551a4d947a0cfd2e89c16a53c0721b6ada4390baa8cf293
data/.gitignore CHANGED
@@ -3,3 +3,4 @@ pkg/*
3
3
  .bundle
4
4
  Gemfile.lock
5
5
  /coverage
6
+ /data/disposable/*
@@ -33,3 +33,6 @@ Metrics/BlockLength:
33
33
  Exclude:
34
34
  - bin/**/*
35
35
  - "*.gemspec"
36
+
37
+ Layout/EmptyLinesAroundAttributeAccessor:
38
+ Enabled: false
data/README.md CHANGED
@@ -32,8 +32,7 @@ class User < ActiveRecord::Base
32
32
  end
33
33
  ```
34
34
 
35
- By default, it rejects disposable e-mails (e.g. mailinator). This loads ~15kb,
36
- but you can disable this validation by setting `disposable: true`.
35
+ By default, it rejects disposable e-mails (e.g. mailinator). This loads a lot of data (~1.7MB), but you can disable this validation by setting `disposable: true`.
37
36
 
38
37
  ```ruby
39
38
  class User < ActiveRecord::Base
@@ -56,7 +55,7 @@ class User < ActiveRecord::Base
56
55
  validates_url_format_of :site
57
56
 
58
57
  # validates TLD against list of valid TLD.
59
- # Loads ~5kb of text.
58
+ # Loads ~10KB of text.
60
59
  validates_url_format_of :site, tld: true
61
60
  end
62
61
  ```
@@ -153,7 +152,8 @@ A valid username/subdomain follows the hostname label validation:
153
152
  - cannot begin or end with a hyphen
154
153
  - cannot consist of numeric values only
155
154
 
156
- The compiled list will be used for both username and subdomain validations.
155
+ The compiled list will be used for both username and subdomain validations.
156
+ This validation loads ~20KB of text.
157
157
 
158
158
  ```ruby
159
159
  class Server < ActiveRecord::Base
@@ -4,18 +4,22 @@
4
4
  require_relative "helpers"
5
5
 
6
6
  def ten_minute_mail
7
- path = "disposable/10minutemail.json"
8
- url = "https://10minutemail.com/10MinuteMail/resources/session/address"
7
+ path = "disposable/10minutemail.txt"
8
+ url = "https://10minutemail.com/session/address"
9
9
 
10
- refresh_list(url: url, path: path) do |response|
11
- _account, host = response.body.split("@")
10
+ 20.times do
11
+ refresh_list(url: url, path: path) do |response|
12
+ _account, host = response.data.fetch("address").split("@")
13
+
14
+ [host]
15
+ end
12
16
 
13
- [host]
17
+ sleep random_timeout
14
18
  end
15
19
  end
16
20
 
17
21
  def temp_mail
18
- path = "disposable/tempmail.json"
22
+ path = "disposable/tempmail.txt"
19
23
  url = "https://api4.temp-mail.org/request/domains/format/json"
20
24
 
21
25
  refresh_list(url: url, path: path) do |response|
@@ -24,7 +28,7 @@ def temp_mail
24
28
  end
25
29
 
26
30
  def temp_mail_address
27
- path = "disposable/tempmailaddress.json"
31
+ path = "disposable/tempmailaddress.txt"
28
32
  url = "https://www.tempmailaddress.com/index/index"
29
33
 
30
34
  refresh_list(url: url, path: path) do |response|
@@ -37,7 +41,7 @@ def temp_mail_address
37
41
  end
38
42
 
39
43
  def tempmail_io
40
- path = "disposable/tempmail_io.json"
44
+ path = "disposable/tempmail_io.txt"
41
45
  url = "https://api.internal.temp-mail.io/api/v2/domains"
42
46
 
43
47
  refresh_list(url: url, path: path) do |response|
@@ -46,69 +50,67 @@ def tempmail_io
46
50
  end
47
51
 
48
52
  def gmailnator
49
- path = "disposable/gmailnator.json"
50
- url = "https://gmailnator.com/index/indexquery"
51
-
52
- refresh_list(
53
- verb: :post,
54
- url: url,
55
- path: path,
56
- params: {action: "GenerateEmail"}
57
- ) do |response|
58
- email = response.body.gsub(/(\+[^@]+)/, "")
59
- [email]
60
- end
61
- end
53
+ emails = []
62
54
 
63
- def prepare_patterns(domains)
64
- new_domains = domains.map do |domain|
65
- {
66
- domain: domain,
67
- processed: domain.gsub(/\..*?$/, "")
68
- }
69
- end
55
+ 5.times do
56
+ url = "https://gmailnator.com/bulk-emails"
57
+ default_headers = {"user-agent" => USER_AGENT.sample}
70
58
 
71
- stats = new_domains
72
- .count_by {|info| info[:processed] }
73
- .select {|_processed, count| count > 2 }
59
+ response = Aitch.get(url: url, headers: default_headers)
74
60
 
75
- stats_map = stats.each_with_object({}) do |(name, count), buffer|
76
- buffer[name] = count
77
- end
61
+ throw "Received #{response.status} when getting CSRF token" unless response.ok?
78
62
 
79
- domains = new_domains
80
- .reject {|info| stats_map[info[:processed]] }
81
- .map {|info| info[:domain] }
82
- domains += stats_map.keys.map {|processed| "/#{processed}\\..+$/" }
63
+ cookie_header = response.headers["set-cookie"]
64
+ attr = response.data.css("#csrf_token").first
65
+ csrf_token = attr[:value]
66
+ csrf_field = attr[:name]
83
67
 
84
- domains.select {|domain| domain.start_with?("/") }
85
- end
68
+ response = Aitch.post(
69
+ url: url,
70
+ params: {email_list: "1000", email: [3], csrf_field => csrf_token},
71
+ headers: default_headers.merge({"cookie" => cookie_header})
72
+ )
86
73
 
87
- def domain_scraping(name, url, selector)
88
- puts "=> scraping #{url}"
74
+ throw "Received #{response.status} when fetching list" unless response.ok?
89
75
 
90
- selector, value_selector = selector.split("::")
91
- path = "disposable/#{name}.json"
92
- host_regex = /@?(.*?(\.[^.]+)+)/
76
+ emails += response.data.css("#email-list-message a").map do |node|
77
+ mailbox, domain = node.text.gsub(/\+[^@]+/, "").split("@")
78
+ mailbox = mailbox.gsub(/\./m, "")
79
+ "#{mailbox}@#{domain}"
80
+ end
93
81
 
94
- refresh_list(url: url, path: path) do |response|
95
- new_domains = response
96
- .data
97
- .css(selector)
98
- .map {|element| process_scraping(element, value_selector) }
99
-
100
- new_domains = new_domains
101
- .map(&:squeeze)
102
- .map(&:strip)
103
- .reject(&:empty?)
104
- .map {|domain| domain[host_regex, 1]&.squeeze&.tr("@", "") }
105
- .reject(&:nil?)
106
- .reject(&:empty?)
107
- .map {|domain| domain.gsub(/\s*\((.*?)\)/, "") }
108
-
109
- raise "No #{name} hosts found" if new_domains.empty?
110
-
111
- new_domains
82
+ sleep random_timeout
83
+ end
84
+
85
+ append_to_file("disposable/gmailnator.txt", emails)
86
+ end
87
+
88
+ def domain_scraping(name, url, selector)
89
+ timeout(10) do
90
+ puts "=> Scraping #{url}"
91
+
92
+ selector, value_selector = selector.split("::")
93
+ path = "disposable/#{name}.txt"
94
+ host_regex = /@?(.*?(\.[^.]+)+)/
95
+
96
+ refresh_list(url: url, path: path) do |response|
97
+ new_domains = response
98
+ .data
99
+ .css(selector)
100
+ .map {|element| process_scraping(element, value_selector) }
101
+
102
+ new_domains = new_domains
103
+ .map(&:squish)
104
+ .reject(&:empty?)
105
+ .map {|domain| domain[host_regex, 1]&.squish&.tr("@", "") }
106
+ .reject(&:nil?)
107
+ .reject(&:empty?)
108
+ .map {|domain| domain.gsub(/\s*\((.*?)\)/, "") }
109
+
110
+ raise "No #{name} hosts found" if new_domains.empty?
111
+
112
+ new_domains
113
+ end
112
114
  end
113
115
  rescue StandardError => error
114
116
  puts "=> [ERROR] Unable to scrape #{url}; #{error.class}: #{error.message}"
@@ -139,7 +141,7 @@ def load_github_url(url)
139
141
  puts "=> Fetching #{url}"
140
142
 
141
143
  basename = URI.parse(url).path[%r{/([^/]+/[^/]+)}, 1].tr("/", "_").tr("-", "_")
142
- path = "disposable/#{basename}.json"
144
+ path = "disposable/#{basename}.txt"
143
145
  domains = load_file(path)
144
146
 
145
147
  ext = File.extname(url)
@@ -155,48 +157,43 @@ def load_github_url(url)
155
157
 
156
158
  append_to_file(path, domains)
157
159
  domains
158
- rescue error
160
+ rescue StandardError => error
159
161
  puts "=> Unable to load #{url}; #{error.class}: #{error.message}"
160
162
  []
161
163
  end
162
164
 
163
- domains = []
164
165
  threads = []
165
166
 
166
- threads << thread { domains += load_github_url("https://raw.githubusercontent.com/ivolo/disposable-email-domains/master/index.json") }
167
- threads << thread { domains += load_github_url("https://raw.githubusercontent.com/andreis/disposable-email-domains/master/domains.json") }
168
- threads << thread { domains += load_github_url("https://raw.githubusercontent.com/FGRibreau/mailchecker/master/list.txt") }
169
- threads << thread { domains += load_github_url("https://raw.githubusercontent.com/willwhite/freemail/master/data/disposable.txt") }
170
- threads << thread { domains += load_github_url("https://raw.githubusercontent.com/maxmalysh/disposable-emails/master/disposable_emails/data/domains.txt") }
171
- threads << thread { domains += load_github_url("https://raw.githubusercontent.com/sneakykiwi/LeagueCreatorPublic/master/emails.txt") }
172
- threads << thread { domains += load_github_url("https://raw.githubusercontent.com/jespernissen/disposable-maildomain-list/master/disposable-maildomain-list.txt") }
173
- threads << thread { domains += load_github_url("https://raw.githubusercontent.com/wesbos/burner-email-providers/master/emails.txt") }
174
- threads << thread { domains += load_github_url("https://gist.github.com/fnando/dafe542cac13f831bbf5521a55248116/raw/f364d880cee5c878531e4f48be1744ff6ec84cb8/disposable.txt") }
175
- threads << thread { domains += ten_minute_mail }
176
- threads << thread { domains += temp_mail }
177
- threads << thread { domains += temp_mail_address }
178
- threads << thread { domains += tempmail_io }
179
- threads << thread { domains += load_file("disposable/disposable_manually_added.json") }
180
- threads << thread { domains += domain_scraping("guerrillamail", "https://www.guerrillamail.com/", "select option::attr(value)") }
181
- threads << thread { domains += domain_scraping("moakt", "https://www.moakt.com", "select option::attr(value)") }
182
- threads << thread { domains += domain_scraping("tempr", "https://tempr.email/", "select[name=DomainId] option::text()") }
183
- threads << thread { domains += domain_scraping("ically", "https://ically.net/", "select[name=domain] option::text()") }
184
- threads << thread { domains += domain_scraping("yepmail", "https://yepmail.co/", "select[name=domain] option::text()") }
185
- threads << thread { domains += domain_scraping("fake_email_generator", "https://fakemailgenerator.net", "[data-mailhost]::attr(data-mailhost)") }
186
- threads << thread { domains += domain_scraping("tempemails", "https://www.tempemails.net/", "select[name=domain] option::attr(value)") }
187
- threads << thread { domains += domain_scraping("clipmails", "https://clipmails.com/", "select[name=domain] option::attr(value)") }
188
- threads << thread { domains += domain_scraping("1secmail", "https://www.1secmail.com/", "select[id=domain] option::attr(value)") }
189
- threads << thread { domains += domain_scraping("emailfake", "https://generator.email", ".tt-suggestion p::text()") }
190
- threads << thread { domains += domain_scraping("emailfake", "https://emailfake.com/", ".tt-suggestion p::text()") }
191
- threads << thread { domains += domain_scraping("emailfake", "https://email-fake.com/", ".tt-suggestion p::text()") }
192
- threads << thread { domains += domain_scraping("receivemail", "https://www.receivemail.org/", "select[name=domain] option::text()") }
193
- threads << thread { domains += domain_scraping("itemp", "https://itemp.email", "select[name=domain] option::text()") }
194
- threads << thread { domains += domain_scraping("tempomail", "https://tempomail.org", "select[name=domain] option::text()") }
195
- threads << thread { domains += domain_scraping("tempmail", "https://tempmail.top", "select[name=domain] option::text()") }
196
- threads << thread { domains += domain_scraping("cs", "https://www.cs.email", "select[id=gm-host-select] option::text()") }
197
- threads << thread { domains += domain_scraping("tempmail", "https://tempmail.io/settings/", "select[id=domain] option::text()") }
198
- threads << thread { domains += domain_scraping("tempemail", "https://tempemail.co", "select[name=email_domain] option::text()") }
199
- threads << thread { domains += domain_scraping("tmail", "https://mytemp-email.com/", "a.domain-selector::text()") }
167
+ threads << thread { load_github_url("https://raw.githubusercontent.com/ivolo/disposable-email-domains/master/index.json") }
168
+ threads << thread { load_github_url("https://raw.githubusercontent.com/andreis/disposable-email-domains/master/domains.json") }
169
+ threads << thread { load_github_url("https://raw.githubusercontent.com/FGRibreau/mailchecker/master/list.txt") }
170
+ threads << thread { load_github_url("https://raw.githubusercontent.com/willwhite/freemail/master/data/disposable.txt") }
171
+ threads << thread { load_github_url("https://raw.githubusercontent.com/maxmalysh/disposable-emails/master/disposable_emails/data/domains.txt") }
172
+ threads << thread { load_github_url("https://raw.githubusercontent.com/jespernissen/disposable-maildomain-list/master/disposable-maildomain-list.txt") }
173
+ threads << thread { load_github_url("https://raw.githubusercontent.com/wesbos/burner-email-providers/master/emails.txt") }
174
+ threads << thread { load_github_url("https://gist.github.com/fnando/dafe542cac13f831bbf5521a55248116/raw/disposable.txt") }
175
+ threads << thread { ten_minute_mail }
176
+ threads << thread { temp_mail }
177
+ threads << thread { temp_mail_address }
178
+ threads << thread { tempmail_io }
179
+ threads << thread { load_file("disposable/disposable_manually_added.txt") }
180
+ threads << thread { domain_scraping("guerrillamail", "https://www.guerrillamail.com/", "select option::attr(value)") }
181
+ threads << thread { domain_scraping("moakt", "https://www.moakt.com", "select option::attr(value)") }
182
+ threads << thread { domain_scraping("tempr", "https://tempr.email/", "select[name=DomainId] option::text()") }
183
+ threads << thread { domain_scraping("yepmail", "https://yepmail.co/", "select[name=domain] option::text()") }
184
+ threads << thread { domain_scraping("fake_email_generator", "https://fakemailgenerator.net", "[data-mailhost]::attr(data-mailhost)") }
185
+ threads << thread { domain_scraping("tempemails", "https://www.tempemails.net/", "select[name=domain] option::attr(value)") }
186
+ threads << thread { domain_scraping("clipmails", "https://clipmails.com/", "select[name=domain] option::attr(value)") }
187
+ threads << thread { domain_scraping("1secmail", "https://www.1secmail.com/", "select[id=domain] option::attr(value)") }
188
+ threads << thread { domain_scraping("emailfake", "https://generator.email", ".tt-suggestion p::text()") }
189
+ threads << thread { domain_scraping("emailfake", "https://emailfake.com/", ".tt-suggestion p::text()") }
190
+ threads << thread { domain_scraping("emailfake", "https://email-fake.com/", ".tt-suggestion p::text()") }
191
+ threads << thread { domain_scraping("receivemail", "https://www.receivemail.org/", "select[name=domain] option::text()") }
192
+ threads << thread { domain_scraping("itemp", "https://itemp.email", "select[name=domain] option::text()") }
193
+ threads << thread { domain_scraping("cs", "https://www.cs.email", "select[id=gm-host-select] option::text()") }
194
+ threads << thread { domain_scraping("tempmail", "https://tempmail.io/settings/", "select[id=domain] option::text()") }
195
+ threads << thread { domain_scraping("tempemail", "https://tempemail.co", "select[name=email_domain] option::text()") }
196
+ threads << thread { domain_scraping("tmail", "https://mytemp-email.com/", "a.domain-selector::text()") }
200
197
 
201
198
  threads.each_slice(5) do |slice|
202
199
  slice.each(&:join)
@@ -204,17 +201,30 @@ end
204
201
 
205
202
  threads.clear
206
203
 
207
- # Unprocessed domains
208
- save_file("disposable_raw.json", domains)
204
+ domains = []
205
+
206
+ puts "=> Loading disposable_domains.txt"
207
+ domains += File.read("#{__dir__}/../data/disposable_domains.txt").lines.map(&:chomp)
208
+
209
+ puts "=> Loading disposable/*.txt"
210
+ Dir["./data/disposable/**/*.txt"].map do |file|
211
+ file = File.expand_path(file)
212
+ domains += File.read(file).lines.map(&:chomp).flatten.compact
213
+ end
209
214
 
210
- domains.map!(&:downcase)
211
- domains = root_domains(domains)
215
+ ignore_domains = %w[gmail.com hotmail.com]
212
216
 
213
- # All disposable domains, unfiltered
214
- save_file("disposable.json", domains)
217
+ puts "=> Normalize domains (count: #{domains.size})"
218
+ domains = domains
219
+ .uniq
220
+ .map {|domain| RootDomain.call(domain.split("@").last.downcase) }
221
+ .compact
222
+ .uniq
223
+ .reject {|domain| ignore_domains.include?(domain) }
215
224
 
216
- # All disposable domains, with patterns
217
- save_file("disposable_patterns.json", prepare_patterns(domains))
225
+ puts "=> Saving domains (count: #{domains.size})"
226
+ save_file("disposable_domains.txt", domains)
218
227
 
219
- # Emails being used as proxy.
220
- save_file("disposable_emails.json", gmailnator)
228
+ emails = gmailnator
229
+ puts "=> Saving email proxies (count: #{emails.size})"
230
+ save_file("disposable_emails.txt", emails)
@@ -10,11 +10,11 @@ tlds.map!(&:downcase)
10
10
  tlds.map!(&:strip)
11
11
  tlds.map! {|tld| SimpleIDN.to_ascii(tld) }
12
12
 
13
- save_file("tld.json", tlds)
13
+ save_file("tld.txt", tlds)
14
14
 
15
15
  country_tlds = JSON.parse(http_request(:get, "https://github.com/samayo/country-json/raw/master/src/country-by-domain-tld.json").body, symbolize_names: true)
16
16
  country_tlds = country_tlds
17
17
  .reject {|info| info[:tld].nil? }
18
18
  .map {|info| info[:tld].gsub(/^\./, "") }
19
19
 
20
- save_file("country_tlds.json", country_tlds)
20
+ save_file("country_tlds.txt", country_tlds)
@@ -0,0 +1,235 @@
1
+ ad
2
+ ae
3
+ af
4
+ ag
5
+ ai
6
+ al
7
+ am
8
+ an
9
+ ao
10
+ aq
11
+ ar
12
+ as
13
+ at
14
+ au
15
+ aw
16
+ az
17
+ ba
18
+ bb
19
+ bd
20
+ be
21
+ bf
22
+ bg
23
+ bh
24
+ bi
25
+ bj
26
+ bm
27
+ bn
28
+ bo
29
+ br
30
+ bs
31
+ bt
32
+ bv
33
+ bw
34
+ by
35
+ bz
36
+ ca
37
+ cc
38
+ cd
39
+ cf
40
+ cg
41
+ ch
42
+ ci
43
+ ck
44
+ cl
45
+ cm
46
+ cn
47
+ co
48
+ cr
49
+ cu
50
+ cv
51
+ cx
52
+ cy
53
+ cz
54
+ de
55
+ dj
56
+ dk
57
+ dm
58
+ do
59
+ dz
60
+ ec
61
+ ee
62
+ eg
63
+ eh
64
+ er
65
+ es
66
+ et
67
+ fi
68
+ fj
69
+ fk
70
+ fr
71
+ ga
72
+ gb
73
+ gd
74
+ ge
75
+ gf
76
+ gh
77
+ gi
78
+ gl
79
+ gm
80
+ gn
81
+ gp
82
+ gq
83
+ gr
84
+ gs
85
+ gt
86
+ gu
87
+ gw
88
+ gy
89
+ hk
90
+ hm
91
+ hn
92
+ hr
93
+ ht
94
+ hu
95
+ id
96
+ ie
97
+ il
98
+ in
99
+ io
100
+ iq
101
+ ir
102
+ is
103
+ it
104
+ jm
105
+ jo
106
+ jp
107
+ ke
108
+ kg
109
+ kh
110
+ ki
111
+ km
112
+ kn
113
+ kp
114
+ kr
115
+ kw
116
+ ky
117
+ kz
118
+ la
119
+ lb
120
+ lc
121
+ li
122
+ lk
123
+ lr
124
+ ls
125
+ lt
126
+ lu
127
+ lv
128
+ ly
129
+ ma
130
+ mc
131
+ md
132
+ mg
133
+ mh
134
+ mk
135
+ ml
136
+ mm
137
+ mn
138
+ mo
139
+ mp
140
+ mq
141
+ mr
142
+ ms
143
+ mt
144
+ mu
145
+ mv
146
+ mw
147
+ mx
148
+ my
149
+ mz
150
+ na
151
+ nc
152
+ ne
153
+ nf
154
+ ng
155
+ ni
156
+ nl
157
+ no
158
+ np
159
+ nr
160
+ nu
161
+ nz
162
+ om
163
+ pa
164
+ pe
165
+ pf
166
+ pg
167
+ ph
168
+ pk
169
+ pl
170
+ pm
171
+ pn
172
+ pr
173
+ ps
174
+ pt
175
+ pw
176
+ py
177
+ qa
178
+ re
179
+ ro
180
+ ru
181
+ rw
182
+ sa
183
+ sb
184
+ sc
185
+ sd
186
+ se
187
+ sg
188
+ sh
189
+ si
190
+ sj
191
+ sk
192
+ sl
193
+ sm
194
+ sn
195
+ so
196
+ sr
197
+ ss
198
+ st
199
+ sv
200
+ sy
201
+ sz
202
+ tc
203
+ td
204
+ tf
205
+ tg
206
+ th
207
+ tj
208
+ tk
209
+ tl
210
+ tm
211
+ tn
212
+ to
213
+ tr
214
+ tt
215
+ tv
216
+ tz
217
+ ua
218
+ ug
219
+ us
220
+ uy
221
+ uz
222
+ va
223
+ vc
224
+ ve
225
+ vg
226
+ vi
227
+ vn
228
+ vu
229
+ wf
230
+ ws
231
+ ye
232
+ yt
233
+ za
234
+ zm
235
+ zw