truemail 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d2c535ef7ee89b62217b36df966ba3d94422498a0431d87218540d4d12073ad9
4
- data.tar.gz: 462b13314a2d223d7db3b305d108a8a8f052197fc5cbd311001dfdfea2b6bddc
3
+ metadata.gz: f597eb293f10835d12b766da633b9d1808424760924a89472416729e60241c23
4
+ data.tar.gz: a4fe4b473856a44d312bfb15fdd831a412d5764fedc33274f5923b75b59b5ce0
5
5
  SHA512:
6
- metadata.gz: b2dc0174969794428067aba261022536cbca33895686b89f88be6ee83d4c5c97caa1e8ea6a28c91277e2204e1b9c33ae412faa26137b1c609886540526a68aac
7
- data.tar.gz: 01f89de5cb1cbc2f94cbeb82b996bcabd5447b7d2791c985070a8cd053ef936cc961d7d7d4b17902ceda923e870a8c572e6fae55c4148c510eba52e65a931cb9
6
+ metadata.gz: dd936cd55b94200bb344d78236527f31cb298e2a453ca660f4d825ba0538c9d6057e730b975319f68130fe64d847cdacb7937a4dbb0344f3ceaf43efb7f3b7b0
7
+ data.tar.gz: 3d8814c197319adb5fb8e9fb2e1c5b5d46dee8adb3bab204199d98b78137b9cbdf8ca28873405b65af8c570ff499a93981851a2bd5e962f1c5910c9303c00125
data/.reek.yml CHANGED
@@ -21,18 +21,21 @@ detectors:
21
21
  Attribute:
22
22
  exclude:
23
23
  - Truemail::Configuration#smtp_safe_check
24
- - Truemail::Validate::ResolverExecutionWrapper#attempts
24
+ - Truemail::Wrapper#attempts
25
25
 
26
26
  UtilityFunction:
27
27
  exclude:
28
28
  - Truemail::Validate::Smtp::Request#compose_from
29
29
  - Truemail::Validator#select_validation_type
30
30
  - Truemail::Validate::Mx#null_mx?
31
+ - Truemail::Validate::Mx#a_record
32
+ - Truemail::Audit::Ptr#current_host_address
31
33
 
32
34
  ControlParameter:
33
35
  exclude:
34
36
  - Truemail::GenerateEmailHelper#calculate_email_size
35
- - Truemail::Validate::Base#success
37
+ - Truemail::Worker#success
38
+ - Truemail#raise_unless
36
39
 
37
40
  FeatureEnvy:
38
41
  exclude:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- truemail (0.1.6)
4
+ truemail (0.1.7)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Maintainability](https://api.codeclimate.com/v1/badges/657aa241399927dcd2e2/maintainability)](https://codeclimate.com/github/rubygarage/truemail/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/657aa241399927dcd2e2/test_coverage)](https://codeclimate.com/github/rubygarage/truemail/test_coverage) [![Gem Version](https://badge.fury.io/rb/truemail.svg)](https://badge.fury.io/rb/truemail) [![CircleCI](https://circleci.com/gh/rubygarage/truemail/tree/master.svg?style=svg)](https://circleci.com/gh/rubygarage/truemail/tree/master) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md)
4
4
 
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.
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. Also Truemail gem allows performing an audit of the host in which runs.
6
6
 
7
7
  ## Features
8
8
 
@@ -137,7 +137,7 @@ Truemail.configuration
137
137
 
138
138
  #### Regex validation
139
139
 
140
- 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.
140
+ Validation with regex pattern is the first validation level. By default this validation not performs strictly following RFC 5322 standard, so you can override Truemail default regex pattern if you want.
141
141
 
142
142
  Example of usage:
143
143
 
@@ -383,6 +383,30 @@ Truemail.validate('email@example.com')
383
383
  @validation_type=:smtp>
384
384
  ```
385
385
 
386
+ ### Host audit features
387
+
388
+ Truemail gem allows performing an audit of the host in which runs. Only PTR record audit performs for today.
389
+
390
+ #### PTR audit
391
+
392
+ So what is a PTR record? A PTR record, or pointer record, enables someone to perform a reverse DNS lookup. This allows them to determine your domain name based on your IP address. Because generic domain names without a PTR are often associated with spammers, incoming mail servers identify email from hosts without PTR records as spam and you can't verify yours emails qualitatively.
393
+
394
+ ```ruby
395
+ Truemail.host_audit
396
+ # Everything is good
397
+ => #<Truemail::Auditor:0x00005580df358828
398
+ @result=
399
+ #<struct Truemail::Auditor::Result
400
+ warnings={}>>
401
+
402
+ # Has PTR warning
403
+ => #<Truemail::Auditor:0x00005580df358828
404
+ @result=
405
+ #<struct Truemail::Auditor::Result
406
+ warnings=
407
+ {:ptr=>"ptr record does not reference to current verifier domain"}>>
408
+ ```
409
+
386
410
  ### Truemail helpers
387
411
 
388
412
  #### .valid?
data/lib/truemail.rb CHANGED
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'truemail/version'
4
3
  require 'truemail/core'
5
- require 'truemail/configuration'
6
- require 'truemail/validator'
7
4
 
8
5
  module Truemail
9
6
  INCOMPLETE_CONFIG = 'verifier_email is required parameter'
@@ -15,7 +12,7 @@ module Truemail
15
12
  return unless block_given?
16
13
  configuration = Truemail::Configuration.new
17
14
  yield(configuration)
18
- raise ConfigurationError, INCOMPLETE_CONFIG unless configuration.complete?
15
+ raise_unless(configuration.complete?, INCOMPLETE_CONFIG)
19
16
  configuration
20
17
  end
21
18
  end
@@ -29,12 +26,23 @@ module Truemail
29
26
  end
30
27
 
31
28
  def validate(email, **options)
32
- raise ConfigurationError, NOT_CONFIGURED unless configuration
29
+ raise_unless(configuration, NOT_CONFIGURED)
33
30
  Truemail::Validator.new(email, **options).run
34
31
  end
35
32
 
36
33
  def valid?(email, **options)
37
34
  validate(email, **options).result.valid?
38
35
  end
36
+
37
+ def host_audit
38
+ raise_unless(configuration, NOT_CONFIGURED)
39
+ Truemail::Auditor.run
40
+ end
41
+
42
+ private
43
+
44
+ def raise_unless(condition, message)
45
+ raise ConfigurationError, message unless condition
46
+ end
39
47
  end
40
48
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truemail
4
+ module Audit
5
+ class Base < Truemail::Worker
6
+ private
7
+
8
+ def add_warning(message)
9
+ result.warnings[self.class.name.split('::').last.downcase.to_sym] = message
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truemail
4
+ module Audit
5
+ class Ptr < Truemail::Audit::Base
6
+ require 'ipaddr'
7
+ require 'resolv'
8
+
9
+ NOT_FOUND = 'ptr record for current host address was not found'
10
+ NOT_REFERENCES = 'ptr record does not reference to current verifier domain'
11
+
12
+ def run
13
+ return if ptr_records.empty? && add_warning(Truemail::Audit::Ptr::NOT_FOUND)
14
+ return if ptr_references_to_verifier_domain?
15
+ add_warning(Truemail::Audit::Ptr::NOT_REFERENCES)
16
+ end
17
+
18
+ private
19
+
20
+ def current_host_address
21
+ Resolv.getaddress(Socket.gethostname)
22
+ end
23
+
24
+ def current_host_reverse_lookup
25
+ IPAddr.new(current_host_address).reverse
26
+ end
27
+
28
+ def ptr_records
29
+ @ptr_records ||= Truemail::Wrapper.call do
30
+ Resolv::DNS.new.getresources(
31
+ current_host_reverse_lookup, Resolv::DNS::Resource::IN::PTR
32
+ ).map { |ptr_record| ptr_record.name.to_s }
33
+ end || []
34
+ end
35
+
36
+ def ptr_references_to_verifier_domain?
37
+ ptr_records.include?(Truemail.configuration.verifier_domain)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truemail
4
+ class Auditor
5
+ Result = Struct.new(:warnings, keyword_init: true) do
6
+ def initialize(warnings: {}, **args)
7
+ super
8
+ end
9
+ end
10
+
11
+ def self.run
12
+ new.run
13
+ end
14
+
15
+ def result
16
+ @result ||= Truemail::Auditor::Result.new
17
+ end
18
+
19
+ def run
20
+ Truemail::Audit::Ptr.check(result)
21
+ self
22
+ end
23
+ end
24
+ end
data/lib/truemail/core.rb CHANGED
@@ -1,6 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Truemail
4
+ require 'truemail/version'
5
+ require 'truemail/configuration'
6
+ require 'truemail/worker'
7
+ require 'truemail/wrapper'
8
+ require 'truemail/auditor'
9
+ require 'truemail/validator'
10
+
4
11
  class ConfigurationError < StandardError; end
5
12
 
6
13
  class ArgumentError < StandardError
@@ -16,10 +23,14 @@ module Truemail
16
23
  REGEX_DOMAIN_FROM_EMAIL = /\A.+@(.+)\z/
17
24
  end
18
25
 
26
+ module Audit
27
+ require 'truemail/audit/base'
28
+ require 'truemail/audit/ptr'
29
+ end
30
+
19
31
  module Validate
20
32
  require 'truemail/validate/base'
21
33
  require 'truemail/validate/regex'
22
- require 'truemail/validate/resolver_execution_wrapper'
23
34
  require 'truemail/validate/mx'
24
35
  require 'truemail/validate/smtp'
25
36
  require 'truemail/validate/smtp/response'
@@ -2,23 +2,9 @@
2
2
 
3
3
  module Truemail
4
4
  module Validate
5
- class Base
6
- attr_reader :result
7
-
8
- def self.check(result)
9
- new(result).run
10
- end
11
-
12
- def initialize(result)
13
- @result = result
14
- end
15
-
5
+ class Base < Truemail::Worker
16
6
  private
17
7
 
18
- def success(condition)
19
- result.success = condition || false
20
- end
21
-
22
8
  def add_error(message)
23
9
  result.errors[self.class.name.split('::').last.downcase.to_sym] = message
24
10
  end
@@ -24,7 +24,7 @@ module Truemail
24
24
 
25
25
  def mx_lookup
26
26
  host_extractor_methods.any? do |method|
27
- Truemail::Validate::ResolverExecutionWrapper.call { send(method) }
27
+ Truemail::Wrapper.call { send(method) }
28
28
  end
29
29
  end
30
30
 
@@ -41,8 +41,8 @@ module Truemail
41
41
  !mail_servers.include?(Truemail::Validate::Mx::NULL_MX_RECORD)
42
42
  end
43
43
 
44
- def mx_records(domain)
45
- domain_mx_records = Resolv::DNS.new.getresources(domain, Resolv::DNS::Resource::IN::MX)
44
+ def mx_records(hostname)
45
+ domain_mx_records = Resolv::DNS.new.getresources(hostname, Resolv::DNS::Resource::IN::MX)
46
46
  return [Truemail::Validate::Mx::NULL_MX_RECORD] if null_mx?(domain_mx_records)
47
47
  domain_mx_records.sort_by(&:preference).map do |mx_record|
48
48
  Resolv.getaddresses(mx_record.exchange.to_s)
@@ -53,16 +53,24 @@ module Truemail
53
53
  !mail_servers.empty?
54
54
  end
55
55
 
56
+ def domain
57
+ result.domain
58
+ end
59
+
56
60
  def hosts_from_mx_records?
57
- fetch_target_hosts(mx_records(result.domain))
61
+ fetch_target_hosts(mx_records(domain))
58
62
  mail_servers_found?
59
63
  end
60
64
 
65
+ def a_record(hostname)
66
+ Resolv.getaddress(hostname)
67
+ end
68
+
61
69
  def hosts_from_cname_records?
62
- cname_records = Resolv::DNS.new.getresources(result.domain, Resolv::DNS::Resource::IN::CNAME)
70
+ cname_records = Resolv::DNS.new.getresources(domain, Resolv::DNS::Resource::IN::CNAME)
63
71
  return if cname_records.empty?
64
72
  cname_records.each do |cname_record|
65
- host = Resolv.getaddress(cname_record.name.to_s)
73
+ host = a_record(cname_record.name.to_s)
66
74
  hostname = Resolv.getname(host)
67
75
  found_hosts = mx_records(hostname)
68
76
  fetch_target_hosts(found_hosts.empty? ? [host] : found_hosts)
@@ -71,7 +79,7 @@ module Truemail
71
79
  end
72
80
 
73
81
  def host_from_a_record?
74
- fetch_target_hosts([Resolv.getaddress(result.domain)])
82
+ fetch_target_hosts([a_record(domain)])
75
83
  mail_servers_found?
76
84
  end
77
85
  end
@@ -4,7 +4,7 @@ module Truemail
4
4
  module Validate
5
5
  class Smtp < Truemail::Validate::Base
6
6
  ERROR = 'smtp error'
7
- ERROR_BODY = /(?=.*550)(?=.*(user|account)).*/i
7
+ ERROR_BODY = /(?=.*550)(?=.*(user|account|customer|mailbox)).*/i
8
8
 
9
9
  attr_reader :smtp_results
10
10
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Truemail
4
- VERSION = '0.1.6'
4
+ VERSION = '0.1.7'
5
5
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truemail
4
+ class Worker
5
+ attr_reader :result
6
+
7
+ def self.check(result)
8
+ new(result).run
9
+ end
10
+
11
+ def initialize(result)
12
+ @result = result
13
+ end
14
+
15
+ private
16
+
17
+ def success(condition)
18
+ result.success = condition || false
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truemail
4
+ class Wrapper
5
+ attr_accessor :attempts
6
+
7
+ def self.call(&block)
8
+ new.call(&block)
9
+ end
10
+
11
+ def initialize
12
+ @attempts = Truemail.configuration.connection_attempts
13
+ end
14
+
15
+ def call(&block)
16
+ Timeout.timeout(Truemail.configuration.connection_timeout, &block)
17
+ rescue Resolv::ResolvError
18
+ false
19
+ rescue Timeout::Error
20
+ retry unless (self.attempts -= 1).zero?
21
+ false
22
+ end
23
+ end
24
+ end
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.6
4
+ version: 0.1.7
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-04-08 00:00:00.000000000 Z
11
+ date: 2019-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -193,17 +193,21 @@ files:
193
193
  - bin/console
194
194
  - bin/setup
195
195
  - lib/truemail.rb
196
+ - lib/truemail/audit/base.rb
197
+ - lib/truemail/audit/ptr.rb
198
+ - lib/truemail/auditor.rb
196
199
  - lib/truemail/configuration.rb
197
200
  - lib/truemail/core.rb
198
201
  - lib/truemail/validate/base.rb
199
202
  - lib/truemail/validate/mx.rb
200
203
  - lib/truemail/validate/regex.rb
201
- - lib/truemail/validate/resolver_execution_wrapper.rb
202
204
  - lib/truemail/validate/smtp.rb
203
205
  - lib/truemail/validate/smtp/request.rb
204
206
  - lib/truemail/validate/smtp/response.rb
205
207
  - lib/truemail/validator.rb
206
208
  - lib/truemail/version.rb
209
+ - lib/truemail/worker.rb
210
+ - lib/truemail/wrapper.rb
207
211
  - truemail.gemspec
208
212
  homepage: https://github.com/rubygarage/truemail
209
213
  licenses:
@@ -1,26 +0,0 @@
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.connection_attempts
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