truemail 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.reek.yml +4 -0
- data/.rubocop.yml +3 -0
- data/Gemfile.lock +1 -1
- data/README.md +45 -4
- data/lib/truemail.rb +4 -0
- data/lib/truemail/configuration.rb +6 -3
- data/lib/truemail/core.rb +1 -0
- data/lib/truemail/validate/mx.rb +46 -5
- data/lib/truemail/validate/resolver_execution_wrapper.rb +26 -0
- data/lib/truemail/validate/smtp.rb +2 -2
- data/lib/truemail/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8dbdc2b39f8f74cdf2965ab1bde2fd613647247827b64c322250046f1a9a22d5
|
4
|
+
data.tar.gz: f1acdd0da6664afa9e03e4031c45d957fbc765378dbc38553f4bae5f4f9569bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 772bba708fc85b90f2bc3785e40959f067a1ca10bce27c16016ef1f089928f480eb008ec53d18832ce110ae3b5a4c2043ea8c09095af1ce9b92ede14818975d8
|
7
|
+
data.tar.gz: 8d915247fa460b109c18f0e8bd359135c7e4ed738cd5265aa4cec76894910e6199c6e2370d0271674f45597e832501589dfc85e564ffc69846e20f050e20d9ce
|
data/.reek.yml
CHANGED
@@ -11,6 +11,8 @@ detectors:
|
|
11
11
|
exclude:
|
12
12
|
- Truemail::Validate::Smtp::Request#run
|
13
13
|
- Truemail::Validate::Smtp#run
|
14
|
+
- Truemail::Validate::Mx#hosts_from_cname_records
|
15
|
+
- Truemail::Configuration#initialize
|
14
16
|
|
15
17
|
TooManyInstanceVariables:
|
16
18
|
exclude:
|
@@ -19,11 +21,13 @@ detectors:
|
|
19
21
|
Attribute:
|
20
22
|
exclude:
|
21
23
|
- Truemail::Configuration#smtp_safe_check
|
24
|
+
- Truemail::Validate::ResolverExecutionWrapper#attempts
|
22
25
|
|
23
26
|
UtilityFunction:
|
24
27
|
exclude:
|
25
28
|
- Truemail::Validate::Smtp::Request#compose_from
|
26
29
|
- Truemail::Validator#select_validation_type
|
30
|
+
- Truemail::Validate::Mx#mx_records
|
27
31
|
|
28
32
|
ControlParameter:
|
29
33
|
exclude:
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -4,6 +4,12 @@
|
|
4
4
|
|
5
5
|
The Truemail gem helps you validate emails by regex pattern, presence of domain mx-records, and real existence of email account on a current email server.
|
6
6
|
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- Configurable validator, validate only what you need
|
10
|
+
- Zero runtime dependencies
|
11
|
+
- 100% test coverage
|
12
|
+
|
7
13
|
## Installation
|
8
14
|
|
9
15
|
Add this line to your application's Gemfile:
|
@@ -58,6 +64,9 @@ Truemail.configure do |config|
|
|
58
64
|
# Optional parameter. A SMTP server response timeout is equal to 2 ms by default.
|
59
65
|
config.response_timeout = 1
|
60
66
|
|
67
|
+
# Optional parameter. Total of timeout retry. It is equal to 1 by default.
|
68
|
+
config.retry_count = 2
|
69
|
+
|
61
70
|
# Optional parameter. You can predefine which type of validation will be used for domains.
|
62
71
|
# Available validation types: :regex, :mx, :smtp
|
63
72
|
# This configuration will be used over current or default validation type parameter
|
@@ -82,6 +91,7 @@ Truemail.configuration
|
|
82
91
|
@connection_timeout=1,
|
83
92
|
@email_pattern=/regex_pattern/,
|
84
93
|
@response_timeout=1,
|
94
|
+
@retry_count=2,
|
85
95
|
@validation_type_by_domain={},
|
86
96
|
@verifier_domain="somedomain.com",
|
87
97
|
@verifier_email="verifier@example.com"
|
@@ -95,12 +105,15 @@ Truemail.configuration.connection_timeout = 3
|
|
95
105
|
=> 3
|
96
106
|
Truemail.configuration.response_timeout = 4
|
97
107
|
=> 4
|
108
|
+
Truemail.configuration.retry_count = 1
|
109
|
+
=> 1
|
98
110
|
|
99
111
|
Truemail.configuration
|
100
112
|
=> #<Truemail::Configuration:0x000055590cb17b40
|
101
113
|
@connection_timeout=3,
|
102
114
|
@email_pattern=/regex_pattern/,
|
103
115
|
@response_timeout=4,
|
116
|
+
@retry_count=1,
|
104
117
|
@validation_type_by_domain={},
|
105
118
|
@verifier_domain="somedomain.com",
|
106
119
|
@verifier_email="verifier@example.com",
|
@@ -122,7 +135,7 @@ Truemail.configuration
|
|
122
135
|
|
123
136
|
#### Regex validation
|
124
137
|
|
125
|
-
Validation with regex pattern is the first validation level.
|
138
|
+
Validation with regex pattern is the first validation level. By default this validation not performs strictly following RFC 5322 standart, so you can override Truemail default regex pattern if you want.
|
126
139
|
|
127
140
|
Example of usage:
|
128
141
|
|
@@ -174,7 +187,7 @@ Truemail.validate('email@example.com', with: :regex)
|
|
174
187
|
|
175
188
|
#### MX validation
|
176
189
|
|
177
|
-
Validation by MX records is second validation level. It uses Regex validation before
|
190
|
+
Validation by MX records is the second validation level. It uses Regex validation before running itself. When regex validation has completed successfully then runs itself. Truemail MX validation performs strictly following the [RFC 5321](https://tools.ietf.org/html/rfc5321#section-5) standard.
|
178
191
|
|
179
192
|
Example of usage:
|
180
193
|
|
@@ -201,7 +214,7 @@ Truemail.validate('email@example.com', with: :mx)
|
|
201
214
|
|
202
215
|
#### SMTP validation
|
203
216
|
|
204
|
-
SMTP validation is a final, third validation level. This type of validation tries to check real existence of email account on a current email server. This validation runs a chain of previous validations
|
217
|
+
SMTP validation is a final, third validation level. This type of validation tries to check real existence of email account on a current email server. This validation runs a chain of previous validations and if they're complete successfully then runs itself.
|
205
218
|
|
206
219
|
```code
|
207
220
|
[Regex validation] -> [MX validation] -> [SMTP validation]
|
@@ -354,11 +367,39 @@ Truemail.validate('email@example.com')
|
|
354
367
|
@validation_type=:smtp>
|
355
368
|
```
|
356
369
|
|
370
|
+
### Truemail helpers
|
371
|
+
|
372
|
+
#### .valid?
|
373
|
+
|
374
|
+
You can use the ```.valid?``` helper for quick validation of email address. It returns a boolean:
|
375
|
+
|
376
|
+
```ruby
|
377
|
+
# It is shortcut for Truemail.validate('email@example.com').result.valid?
|
378
|
+
Truemail.valid?('email@example.com')
|
379
|
+
=> true
|
380
|
+
```
|
381
|
+
|
382
|
+
### Test environment
|
383
|
+
|
384
|
+
You can stub out that validation for your test environment. Just add RSpec before action:
|
385
|
+
|
386
|
+
```ruby
|
387
|
+
# spec_helper.rb
|
388
|
+
|
389
|
+
RSpec.configure do |config|
|
390
|
+
config.before { allow(Truemail).to receive(:valid?).and_return(true) }
|
391
|
+
# or
|
392
|
+
config.before { allow(Truemail).to receive(:validate).and_return(true) }
|
393
|
+
# or
|
394
|
+
config.before { allow(Truemail).to receive_message_chain(:validate, :result, :valid?).and_return(true) }
|
395
|
+
end
|
396
|
+
```
|
397
|
+
|
398
|
+
---
|
357
399
|
## ToDo
|
358
400
|
|
359
401
|
1. Gem compatibility with Ruby 2.3
|
360
402
|
2. Fail validations logger
|
361
|
-
3. The ability to use a proxy
|
362
403
|
|
363
404
|
## Contributing
|
364
405
|
|
data/lib/truemail.rb
CHANGED
@@ -4,20 +4,23 @@ module Truemail
|
|
4
4
|
class Configuration
|
5
5
|
DEFAULT_CONNECTION_TIMEOUT = 2
|
6
6
|
DEFAULT_RESPONSE_TIMEOUT = 2
|
7
|
+
DEFAULT_RETRY_COUNT = 1
|
7
8
|
|
8
9
|
attr_reader :email_pattern,
|
9
10
|
:verifier_email,
|
10
11
|
:verifier_domain,
|
11
12
|
:connection_timeout,
|
12
13
|
:response_timeout,
|
14
|
+
:retry_count,
|
13
15
|
:validation_type_by_domain
|
14
16
|
|
15
17
|
attr_accessor :smtp_safe_check
|
16
18
|
|
17
19
|
def initialize
|
18
20
|
@email_pattern = Truemail::RegexConstant::REGEX_EMAIL_PATTERN
|
19
|
-
@connection_timeout = DEFAULT_CONNECTION_TIMEOUT
|
20
|
-
@response_timeout = DEFAULT_RESPONSE_TIMEOUT
|
21
|
+
@connection_timeout = Truemail::Configuration::DEFAULT_CONNECTION_TIMEOUT
|
22
|
+
@response_timeout = Truemail::Configuration::DEFAULT_RESPONSE_TIMEOUT
|
23
|
+
@retry_count = Truemail::Configuration::DEFAULT_RETRY_COUNT
|
21
24
|
@validation_type_by_domain = {}
|
22
25
|
@smtp_safe_check = false
|
23
26
|
end
|
@@ -38,7 +41,7 @@ module Truemail
|
|
38
41
|
@verifier_domain = domain.downcase
|
39
42
|
end
|
40
43
|
|
41
|
-
%i[connection_timeout response_timeout].each do |method|
|
44
|
+
%i[connection_timeout response_timeout retry_count].each do |method|
|
42
45
|
define_method("#{method}=") do |argument|
|
43
46
|
raise ArgumentError.new(argument, __method__) unless argument.is_a?(Integer) && argument.positive?
|
44
47
|
instance_variable_set(:"@#{method}", argument)
|
data/lib/truemail/core.rb
CHANGED
@@ -19,6 +19,7 @@ module Truemail
|
|
19
19
|
module Validate
|
20
20
|
require 'truemail/validate/base'
|
21
21
|
require 'truemail/validate/regex'
|
22
|
+
require 'truemail/validate/resolver_execution_wrapper'
|
22
23
|
require 'truemail/validate/mx'
|
23
24
|
require 'truemail/validate/smtp'
|
24
25
|
require 'truemail/validate/smtp/response'
|
data/lib/truemail/validate/mx.rb
CHANGED
@@ -5,21 +5,62 @@ module Truemail
|
|
5
5
|
class Mx < Truemail::Validate::Base
|
6
6
|
require 'resolv'
|
7
7
|
|
8
|
-
ERROR = '
|
8
|
+
ERROR = 'target host(s) not found'
|
9
9
|
|
10
10
|
def run
|
11
11
|
return false unless Truemail::Validate::Regex.check(result)
|
12
12
|
result.domain = result.email[Truemail::RegexConstant::REGEX_DOMAIN_FROM_EMAIL, 1]
|
13
|
-
return true if success(
|
13
|
+
return true if success(mx_lookup)
|
14
14
|
add_error(Truemail::Validate::Mx::ERROR)
|
15
15
|
false
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
19
19
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
20
|
+
def host_extractor_methods
|
21
|
+
%i[hosts_from_mx_records? hosts_from_cname_records? host_from_a_record?]
|
22
|
+
end
|
23
|
+
|
24
|
+
def mx_lookup
|
25
|
+
host_extractor_methods.any? do |method|
|
26
|
+
Truemail::Validate::ResolverExecutionWrapper.call { send(method) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def fetch_target_hosts(hosts)
|
31
|
+
result.mail_servers.push(*hosts)
|
32
|
+
end
|
33
|
+
|
34
|
+
def mx_records(domain)
|
35
|
+
Resolv::DNS.new.getresources(domain, Resolv::DNS::Resource::IN::MX).sort_by(&:preference).map do |mx_record|
|
36
|
+
Resolv.getaddress(mx_record.exchange.to_s)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def mail_servers_found?
|
41
|
+
!result.mail_servers.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
def hosts_from_mx_records?
|
45
|
+
fetch_target_hosts(mx_records(result.domain))
|
46
|
+
mail_servers_found?
|
47
|
+
end
|
48
|
+
|
49
|
+
def hosts_from_cname_records?
|
50
|
+
cname_records = Resolv::DNS.new.getresources(result.domain, Resolv::DNS::Resource::IN::CNAME)
|
51
|
+
return if cname_records.empty?
|
52
|
+
cname_records.each do |cname_record|
|
53
|
+
host = Resolv.getaddress(cname_record.name.to_s)
|
54
|
+
hostname = Resolv.getname(host)
|
55
|
+
found_hosts = mx_records(hostname)
|
56
|
+
fetch_target_hosts(found_hosts.empty? ? [host] : found_hosts)
|
57
|
+
end
|
58
|
+
mail_servers_found?
|
59
|
+
end
|
60
|
+
|
61
|
+
def host_from_a_record?
|
62
|
+
fetch_target_hosts([Resolv.getaddress(result.domain)])
|
63
|
+
mail_servers_found?
|
23
64
|
end
|
24
65
|
end
|
25
66
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Truemail
|
4
|
+
module Validate
|
5
|
+
class ResolverExecutionWrapper
|
6
|
+
attr_accessor :attempts
|
7
|
+
|
8
|
+
def self.call(&block)
|
9
|
+
new.call(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@attempts = Truemail.configuration.retry_count
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(&block)
|
17
|
+
Timeout.timeout(Truemail.configuration.connection_timeout, &block)
|
18
|
+
rescue Resolv::ResolvError
|
19
|
+
false
|
20
|
+
rescue Timeout::Error
|
21
|
+
retry unless (self.attempts -= 1).zero?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -18,7 +18,7 @@ module Truemail
|
|
18
18
|
establish_smtp_connection
|
19
19
|
return true if success(success_response?)
|
20
20
|
result.smtp_debug = smtp_results
|
21
|
-
return true if success(not_includes_user_not_found_errors)
|
21
|
+
return true if success(not_includes_user_not_found_errors?)
|
22
22
|
add_error(Truemail::Validate::Smtp::ERROR)
|
23
23
|
false
|
24
24
|
end
|
@@ -45,7 +45,7 @@ module Truemail
|
|
45
45
|
smtp_results.map(&:response).any?(&:rcptto)
|
46
46
|
end
|
47
47
|
|
48
|
-
def not_includes_user_not_found_errors
|
48
|
+
def not_includes_user_not_found_errors?
|
49
49
|
return unless Truemail.configuration.smtp_safe_check
|
50
50
|
result.smtp_debug.map(&:response).map(&:errors).all? do |errors|
|
51
51
|
next true unless errors.key?(:rcptto)
|
data/lib/truemail/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: truemail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladislav Trotsenko
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -195,6 +195,7 @@ files:
|
|
195
195
|
- lib/truemail/validate/base.rb
|
196
196
|
- lib/truemail/validate/mx.rb
|
197
197
|
- lib/truemail/validate/regex.rb
|
198
|
+
- lib/truemail/validate/resolver_execution_wrapper.rb
|
198
199
|
- lib/truemail/validate/smtp.rb
|
199
200
|
- lib/truemail/validate/smtp/request.rb
|
200
201
|
- lib/truemail/validate/smtp/response.rb
|