truemail 1.7.0 → 1.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +1 -1
- data/.github/ISSUE_TEMPLATE.md +1 -1
- data/.reek.yml +4 -0
- data/.rubocop.yml +177 -6
- data/CHANGELOG.md +157 -3
- data/Gemfile.lock +44 -38
- data/LICENSE.txt +1 -1
- data/README.md +147 -76
- data/lib/truemail.rb +10 -3
- data/lib/truemail/audit/base.rb +8 -0
- data/lib/truemail/audit/dns.rb +26 -0
- data/lib/truemail/audit/ip.rb +28 -0
- data/lib/truemail/audit/ptr.rb +8 -37
- data/lib/truemail/auditor.rb +7 -5
- data/lib/truemail/configuration.rb +8 -2
- data/lib/truemail/core.rb +28 -22
- data/lib/truemail/executor.rb +11 -0
- data/lib/truemail/log/serializer/auditor_json.rb +25 -0
- data/lib/truemail/log/serializer/base.rb +17 -41
- data/lib/truemail/log/serializer/validator_base.rb +45 -0
- data/lib/truemail/log/serializer/{json.rb → validator_json.rb} +1 -1
- data/lib/truemail/log/serializer/{text.rb → validator_text.rb} +2 -2
- data/lib/truemail/logger.rb +1 -1
- data/lib/truemail/validator.rb +4 -4
- data/lib/truemail/version.rb +1 -1
- data/truemail.gemspec +16 -8
- metadata +48 -26
data/lib/truemail/audit/ptr.rb
CHANGED
@@ -3,38 +3,19 @@
|
|
3
3
|
module Truemail
|
4
4
|
module Audit
|
5
5
|
class Ptr < Truemail::Audit::Base
|
6
|
-
|
7
|
-
|
8
|
-
require 'resolv'
|
9
|
-
|
10
|
-
GET_MY_IP_URL = 'https://api.ipify.org'
|
11
|
-
IPIFY_ERROR = 'impossible to detect current host address via third party service'
|
12
|
-
PTR_NOT_FOUND = 'ptr record for current host address was not found'
|
13
|
-
PTR_NOT_REFER = 'ptr record does not reference to current verifier domain'
|
14
|
-
VERIFIER_DOMAIN_NOT_REFER = 'a record of verifier domain not refers to current host address'
|
6
|
+
PTR_NOT_FOUND = 'PTR-record for current host ip address was not found'
|
7
|
+
PTR_NOT_REFER = 'PTR-record does not reference to current verifier domain'
|
15
8
|
|
16
9
|
def run
|
17
|
-
return
|
18
|
-
return if
|
19
|
-
|
20
|
-
return if verifier_domain_refer_to_current_host_address?
|
21
|
-
add_warning(Truemail::Audit::Ptr::VERIFIER_DOMAIN_NOT_REFER)
|
10
|
+
return add_warning(Truemail::Audit::Ptr::PTR_NOT_FOUND) if ptr_records.empty?
|
11
|
+
return if ptr_refer_to_verifier_domain?
|
12
|
+
add_warning(Truemail::Audit::Ptr::PTR_NOT_REFER)
|
22
13
|
end
|
23
14
|
|
24
15
|
private
|
25
16
|
|
26
|
-
def detect_ip_via_ipify
|
27
|
-
Net::HTTP.get(URI(Truemail::Audit::Ptr::GET_MY_IP_URL))
|
28
|
-
end
|
29
|
-
|
30
|
-
def current_host_address
|
31
|
-
@current_host_address ||= Truemail::Wrapper.call(configuration: configuration) do
|
32
|
-
IPAddr.new(detect_ip_via_ipify)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
17
|
def current_host_reverse_lookup
|
37
|
-
|
18
|
+
IPAddr.new(current_host_ip).reverse
|
38
19
|
end
|
39
20
|
|
40
21
|
def ptr_records
|
@@ -45,18 +26,8 @@ module Truemail
|
|
45
26
|
end || []
|
46
27
|
end
|
47
28
|
|
48
|
-
def
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
def a_record
|
53
|
-
Truemail::Wrapper.call(configuration: configuration) do
|
54
|
-
Resolv::DNS.new.getaddress(verifier_domain).to_s
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def verifier_domain_refer_to_current_host_address?
|
59
|
-
a_record.eql?(current_host_address.to_s)
|
29
|
+
def ptr_refer_to_verifier_domain?
|
30
|
+
ptr_records.include?(verifier_domain)
|
60
31
|
end
|
61
32
|
end
|
62
33
|
end
|
data/lib/truemail/auditor.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Truemail
|
4
|
-
class Auditor
|
5
|
-
Result = Struct.new(:warnings, :configuration, keyword_init: true) do
|
4
|
+
class Auditor < Truemail::Executor
|
5
|
+
Result = Struct.new(:current_host_ip, :warnings, :configuration, keyword_init: true) do
|
6
6
|
def initialize(warnings: {}, **args)
|
7
7
|
super
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
attr_reader :result
|
12
|
-
|
13
11
|
def initialize(configuration:)
|
14
12
|
@result = Truemail::Auditor::Result.new(configuration: configuration)
|
15
13
|
end
|
16
14
|
|
17
15
|
def run
|
18
|
-
Truemail::Audit::
|
16
|
+
Truemail::Audit::Ip.check(result)
|
19
17
|
self
|
20
18
|
end
|
19
|
+
|
20
|
+
def as_json
|
21
|
+
Truemail::Log::Serializer::AuditorJson.call(self)
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
@@ -6,6 +6,7 @@ module Truemail
|
|
6
6
|
DEFAULT_RESPONSE_TIMEOUT = 2
|
7
7
|
DEFAULT_CONNECTION_ATTEMPTS = 2
|
8
8
|
DEFAULT_VALIDATION_TYPE = :smtp
|
9
|
+
DEFAULT_LOGGER_OPTIONS = { tracking_event: :error, stdout: false, log_absolute_path: nil }.freeze
|
9
10
|
|
10
11
|
attr_reader :email_pattern,
|
11
12
|
:smtp_error_body_pattern,
|
@@ -71,7 +72,8 @@ module Truemail
|
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
74
|
-
def logger=(
|
75
|
+
def logger=(options)
|
76
|
+
tracking_event, stdout, log_absolute_path = logger_options(options)
|
75
77
|
valid_event = Truemail::Log::Event::TRACKING_EVENTS.key?(tracking_event)
|
76
78
|
stdout_only = stdout && log_absolute_path.nil?
|
77
79
|
file_only = log_absolute_path.is_a?(String)
|
@@ -109,7 +111,7 @@ module Truemail
|
|
109
111
|
end
|
110
112
|
|
111
113
|
def validate_arguments(argument, method)
|
112
|
-
constant = Truemail::RegexConstant.const_get("regex_#{method[/\A.+_(.+)
|
114
|
+
constant = Truemail::RegexConstant.const_get("regex_#{method[/\A.+_(.+)=\z/, 1]}_pattern".upcase)
|
113
115
|
raise_unless(argument, method, constant.match?(argument.to_s))
|
114
116
|
end
|
115
117
|
|
@@ -140,5 +142,9 @@ module Truemail
|
|
140
142
|
check_validation_type(validation_type)
|
141
143
|
end
|
142
144
|
end
|
145
|
+
|
146
|
+
def logger_options(current_options)
|
147
|
+
Truemail::Configuration::DEFAULT_LOGGER_OPTIONS.merge(current_options).values
|
148
|
+
end
|
143
149
|
end
|
144
150
|
end
|
data/lib/truemail/core.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Truemail
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
require_relative '../truemail/version'
|
5
|
+
require_relative '../truemail/configuration'
|
6
|
+
require_relative '../truemail/worker'
|
7
|
+
require_relative '../truemail/executor'
|
8
|
+
require_relative '../truemail/wrapper'
|
9
|
+
require_relative '../truemail/auditor'
|
10
|
+
require_relative '../truemail/validator'
|
11
|
+
require_relative '../truemail/logger'
|
11
12
|
|
12
13
|
ConfigurationError = Class.new(StandardError)
|
14
|
+
TypeError = Class.new(StandardError)
|
13
15
|
|
14
16
|
ArgumentError = Class.new(StandardError) do
|
15
17
|
def initialize(arg_value, arg_name)
|
@@ -29,32 +31,36 @@ module Truemail
|
|
29
31
|
end
|
30
32
|
|
31
33
|
module RegexConstant
|
32
|
-
REGEX_DOMAIN = /[\p{L}0-9]+([
|
33
|
-
REGEX_EMAIL_PATTERN = /(?=\A.{6,255}\z)(\A([\p{L}0-9]+[\w
|
34
|
+
REGEX_DOMAIN = /[\p{L}0-9]+([\-.]{1}[\p{L}0-9]+)*\.\p{L}{2,63}/i.freeze
|
35
|
+
REGEX_EMAIL_PATTERN = /(?=\A.{6,255}\z)(\A([\p{L}0-9]+[\w|\-|.|+]*)@(#{REGEX_DOMAIN})\z)/.freeze
|
34
36
|
REGEX_DOMAIN_PATTERN = /(?=\A.{4,255}\z)(\A#{REGEX_DOMAIN}\z)/.freeze
|
35
37
|
REGEX_DOMAIN_FROM_EMAIL = /\A.+@(.+)\z/.freeze
|
36
38
|
REGEX_SMTP_ERROR_BODY_PATTERN = /(?=.*550)(?=.*(user|account|customer|mailbox)).*/i.freeze
|
37
39
|
end
|
38
40
|
|
39
41
|
module Audit
|
40
|
-
|
41
|
-
|
42
|
+
require_relative '../truemail/audit/base'
|
43
|
+
require_relative '../truemail/audit/ip'
|
44
|
+
require_relative '../truemail/audit/dns'
|
45
|
+
require_relative '../truemail/audit/ptr'
|
42
46
|
end
|
43
47
|
|
44
48
|
module Validate
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
require_relative '../truemail/validate/base'
|
50
|
+
require_relative '../truemail/validate/domain_list_match'
|
51
|
+
require_relative '../truemail/validate/regex'
|
52
|
+
require_relative '../truemail/validate/mx'
|
53
|
+
require_relative '../truemail/validate/smtp'
|
54
|
+
require_relative '../truemail/validate/smtp/response'
|
55
|
+
require_relative '../truemail/validate/smtp/request'
|
52
56
|
end
|
53
57
|
|
54
58
|
module Log
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
+
require_relative '../truemail/log/event'
|
60
|
+
require_relative '../truemail/log/serializer/base'
|
61
|
+
require_relative '../truemail/log/serializer/auditor_json'
|
62
|
+
require_relative '../truemail/log/serializer/validator_base'
|
63
|
+
require_relative '../truemail/log/serializer/validator_text'
|
64
|
+
require_relative '../truemail/log/serializer/validator_json'
|
59
65
|
end
|
60
66
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Truemail
|
4
|
+
module Log
|
5
|
+
module Serializer
|
6
|
+
class AuditorJson < Truemail::Log::Serializer::Base
|
7
|
+
def serialize
|
8
|
+
result.to_json
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def result
|
14
|
+
@result ||=
|
15
|
+
{
|
16
|
+
date: Time.now,
|
17
|
+
current_host_ip: executor_result.current_host_ip,
|
18
|
+
warnings: warnings(executor_result.warnings),
|
19
|
+
configuration: configuration
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -4,47 +4,35 @@ module Truemail
|
|
4
4
|
module Log
|
5
5
|
module Serializer
|
6
6
|
class Base
|
7
|
+
require 'json'
|
8
|
+
|
7
9
|
DEFAULT_GEM_VALUE = 'default gem value'
|
8
10
|
|
9
|
-
def self.call(
|
10
|
-
new(
|
11
|
+
def self.call(executor_instance)
|
12
|
+
new(executor_instance).serialize
|
11
13
|
end
|
12
14
|
|
13
|
-
def initialize(
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@validation_configuration = validation_result.configuration
|
15
|
+
def initialize(executor_instance)
|
16
|
+
@executor_result = executor_instance.result
|
17
|
+
@executor_configuration = executor_result.configuration
|
17
18
|
end
|
18
19
|
|
19
20
|
def serialize; end
|
20
21
|
|
21
22
|
private
|
22
23
|
|
23
|
-
attr_reader :
|
24
|
+
attr_reader :executor_result, :executor_configuration
|
24
25
|
|
25
|
-
def errors
|
26
|
-
|
27
|
-
|
28
|
-
validation_errors
|
26
|
+
def errors(executor_result_target)
|
27
|
+
return if executor_result_target.empty?
|
28
|
+
executor_result_target
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
validation_smtp_debug = validation_result.smtp_debug
|
33
|
-
return unless validation_smtp_debug
|
34
|
-
validation_smtp_debug.map do |smtp_request|
|
35
|
-
smtp_response = smtp_request.response
|
36
|
-
{
|
37
|
-
mail_host: smtp_request.host,
|
38
|
-
port_opened: smtp_response.port_opened,
|
39
|
-
connection: smtp_response.connection,
|
40
|
-
errors: smtp_response.errors
|
41
|
-
}
|
42
|
-
end
|
43
|
-
end
|
31
|
+
alias warnings errors
|
44
32
|
|
45
33
|
%i[validation_type_by_domain whitelisted_domains blacklisted_domains].each do |method|
|
46
34
|
define_method(method) do
|
47
|
-
value =
|
35
|
+
value = executor_configuration.public_send(method)
|
48
36
|
return if value.empty?
|
49
37
|
value
|
50
38
|
end
|
@@ -52,7 +40,7 @@ module Truemail
|
|
52
40
|
|
53
41
|
%i[email_pattern smtp_error_body_pattern].each do |method|
|
54
42
|
define_method(method) do
|
55
|
-
value =
|
43
|
+
value = executor_configuration.public_send(method)
|
56
44
|
default_pattern = Truemail::RegexConstant.const_get(
|
57
45
|
(method.eql?(:email_pattern) ? :regex_email_pattern : :regex_smtp_error_body_pattern).upcase
|
58
46
|
)
|
@@ -64,27 +52,15 @@ module Truemail
|
|
64
52
|
def configuration
|
65
53
|
{
|
66
54
|
validation_type_by_domain: validation_type_by_domain,
|
67
|
-
whitelist_validation:
|
55
|
+
whitelist_validation: executor_configuration.whitelist_validation,
|
68
56
|
whitelisted_domains: whitelisted_domains,
|
69
57
|
blacklisted_domains: blacklisted_domains,
|
70
|
-
|
58
|
+
not_rfc_mx_lookup_flow: executor_configuration.not_rfc_mx_lookup_flow,
|
59
|
+
smtp_safe_check: executor_configuration.smtp_safe_check,
|
71
60
|
email_pattern: email_pattern,
|
72
61
|
smtp_error_body_pattern: smtp_error_body_pattern
|
73
62
|
}
|
74
63
|
end
|
75
|
-
|
76
|
-
def result
|
77
|
-
@result ||=
|
78
|
-
{
|
79
|
-
date: Time.now,
|
80
|
-
email: validation_result.email,
|
81
|
-
validation_type: validation_type,
|
82
|
-
success: validation_result.success,
|
83
|
-
errors: errors,
|
84
|
-
smtp_debug: smtp_debug,
|
85
|
-
configuration: configuration
|
86
|
-
}
|
87
|
-
end
|
88
64
|
end
|
89
65
|
end
|
90
66
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Truemail
|
4
|
+
module Log
|
5
|
+
module Serializer
|
6
|
+
class ValidatorBase < Truemail::Log::Serializer::Base
|
7
|
+
def initialize(executor_instance)
|
8
|
+
@validation_type = executor_instance.validation_type
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :validation_type
|
15
|
+
|
16
|
+
def smtp_debug
|
17
|
+
validation_smtp_debug = executor_result.smtp_debug
|
18
|
+
return unless validation_smtp_debug
|
19
|
+
validation_smtp_debug.map do |smtp_request|
|
20
|
+
smtp_response = smtp_request.response
|
21
|
+
{
|
22
|
+
mail_host: smtp_request.host,
|
23
|
+
port_opened: smtp_response.port_opened,
|
24
|
+
connection: smtp_response.connection,
|
25
|
+
errors: smtp_response.errors
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def result
|
31
|
+
@result ||=
|
32
|
+
{
|
33
|
+
date: Time.now,
|
34
|
+
email: executor_result.email,
|
35
|
+
validation_type: validation_type,
|
36
|
+
success: executor_result.success,
|
37
|
+
errors: errors(executor_result.errors),
|
38
|
+
smtp_debug: smtp_debug,
|
39
|
+
configuration: configuration
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Truemail
|
4
4
|
module Log
|
5
5
|
module Serializer
|
6
|
-
class
|
6
|
+
class ValidatorText < Truemail::Log::Serializer::ValidatorBase
|
7
7
|
ATTEMPT = 'ATTEMPT #'
|
8
8
|
|
9
9
|
def serialize
|
@@ -30,7 +30,7 @@ module Truemail
|
|
30
30
|
|
31
31
|
def collection_printer(collection)
|
32
32
|
collection.inject([]) { |array, hash| array << printer(hash) }.map.with_index do |item, index|
|
33
|
-
"\n#{Truemail::Log::Serializer::
|
33
|
+
"\n#{Truemail::Log::Serializer::ValidatorText::ATTEMPT}#{index + 1}:\n#{item}\n"
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
data/lib/truemail/logger.rb
CHANGED
@@ -15,7 +15,7 @@ module Truemail
|
|
15
15
|
def push(validator_instance)
|
16
16
|
current_event = Truemail::Log::Event.new(event, validator_instance)
|
17
17
|
return unless current_event.valid?
|
18
|
-
create_logs(current_event.log_level, Truemail::Log::Serializer::
|
18
|
+
create_logs(current_event.log_level, Truemail::Log::Serializer::ValidatorText.call(validator_instance))
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
data/lib/truemail/validator.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Truemail
|
4
|
-
class Validator
|
4
|
+
class Validator < Truemail::Executor
|
5
5
|
RESULT_ATTRS = %i[success email domain mail_servers errors smtp_debug configuration].freeze
|
6
6
|
VALIDATION_TYPES = %i[regex mx smtp].freeze
|
7
7
|
|
@@ -16,9 +16,9 @@ module Truemail
|
|
16
16
|
alias_method :valid?, :success
|
17
17
|
end
|
18
18
|
|
19
|
-
attr_reader :validation_type
|
19
|
+
attr_reader :validation_type
|
20
20
|
|
21
|
-
def initialize(email, with: nil
|
21
|
+
def initialize(email, configuration:, with: nil)
|
22
22
|
with ||= configuration.default_validation_type
|
23
23
|
raise Truemail::ArgumentError.new(with, :argument) unless Truemail::Validator::VALIDATION_TYPES.include?(with)
|
24
24
|
@result = Truemail::Validator::Result.new(email: email, configuration: configuration)
|
@@ -33,7 +33,7 @@ module Truemail
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def as_json
|
36
|
-
Truemail::Log::Serializer::
|
36
|
+
Truemail::Log::Serializer::ValidatorJson.call(self)
|
37
37
|
end
|
38
38
|
|
39
39
|
private
|