cogi_email 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9d200209729f74272e98dc1b8dcebdd549ac84d0
4
+ data.tar.gz: 9841848a372166e0394690efc712f276df25e8e4
5
+ SHA512:
6
+ metadata.gz: 154d7b0085d49df9c40b0a22700072990cb9cea25f2da5193d45bbae133bdce97192772027fc5bd5a1c97525cf13fc99005464d298f0980596c6942f99b19961
7
+ data.tar.gz: 57c94d16cc90a79961a0a1137448b8181b999ba675d83500182347a828271a864a195f4db5d7a181c9ddf327a52886eca6849c21c402259de609a667ad830dd3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 Hoa Hoang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # PhonyRails [![Build Status](https://travis-ci.org/hoahm/cogi_email.svg?branch=master)](https://travis-ci.org/hoahm/cogi_email) [![Coverage Status](https://coveralls.io/repos/github/hoahm/cogi_email/badge.svg)](https://coveralls.io/github/hoahm/cogi_email) [![GitHub issues](https://img.shields.io/github/issues/hoahm/cogi_email.svg)](https://github.com/hoahm/cogi_email/issues) [![Gem Version](https://badge.fury.io/rb/cogi_email.svg)](https://badge.fury.io/rb/cogi_email)
2
+
3
+ This gem provide you library to validate, parsing and format email. Check is email is real or not.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'cogi_email'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```
22
+ $ gem install cogi_email
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Validation
28
+
29
+ Check if a string is a valid email address.
30
+
31
+ ```ruby
32
+ CogiEmail.validate? 'nobi.younet@gmail.com' # => true
33
+ ```
34
+
35
+
36
+ ### Normalization
37
+
38
+ Normalize phone numer to international format.
39
+
40
+ ```ruby
41
+ CogiEmail.normalize 'Nobi.younet@gmail.com' # => 'nobi.younet@gmail.com'
42
+ CogiEmail.normalize '(Nobi)<nobi.younet@gmail.com>' # => 'nobi.younet@gmail.com'
43
+ ```
44
+
45
+ ### Validate email domain
46
+
47
+ Check if email domain is valid by making a DNS lookup.
48
+
49
+ ```ruby
50
+ CogiEmail.valid_email_domain? 'nobi.younet@gmail.com' # => true
51
+ CogiEmail.valid_email_domain? 'nobi.younet@localhost' # => false
52
+ ```
53
+
54
+ ### Check if real email
55
+
56
+ Check if an email address is real or not.
57
+
58
+ An email address is real if:
59
+ - Is valid
60
+ - Has MX DNS record
61
+ - Can send a test email
62
+
63
+ ```ruby
64
+ CogiEmail.real_email? 'nobi.younet@gmail.com' # => true
65
+ CogiEmail.real_email? 'nobi.younet@localhost' # => false
66
+ ```
67
+
68
+ ## Credit
69
+
70
+ Thank you [Kamil Ciemniewski](https://github.com/kamilc) so much for writing [email_verifier](https://github.com/kamilc/email_verifier) gem. I reference his gem to re-writing email checker for this gem.
71
+
72
+ ## Contributing
73
+
74
+ 1. Fork it
75
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
76
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
77
+ 4. Push to the branch (`git push origin my-new-feature`)
78
+ 5. Create new Pull Request
79
+
80
+ Don't forget to add tests and run rspec before creating a pull request :)
81
+
82
+ See all contributors on https://github.com/hoahm/cogi_email/graphs/contributors.
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ Dir.glob('tasks/**/*.rake').each(&method(:import))
4
+
5
+ task default: :spec
data/lib/cogi_email.rb ADDED
@@ -0,0 +1,107 @@
1
+ require 'cogi_email/checker'
2
+ require 'cogi_email/error'
3
+ require 'cogi_email/version'
4
+ require 'mail'
5
+ require 'resolv'
6
+
7
+ module CogiEmail
8
+ # Check if a string is a valid email address.
9
+ #
10
+ # This library is so strict to ensure that all email is clean and deliverable.
11
+ #
12
+ # References:
13
+ # https://www.jochentopf.com/email/
14
+ # https://fightingforalostcause.net/content/misc/2006/compare-email-regex.php
15
+ #
16
+ # @param [String] email Email address
17
+ #
18
+ # @return [Boolean] True if it is in a valid email, otherwise False
19
+ #
20
+ # @example
21
+ #
22
+ # CogiEmail.validate?('peter_brown@example.com') # => true
23
+ # CogiEmail.validate?('peter-brown@example.com') # => true
24
+ # CogiEmail.validate?('peter.brown@example.com') # => true
25
+ # CogiEmail.validate?('peter brown@example.com') # => false
26
+ # CogiEmail.validate?('peter_brown@@example.com') # => false
27
+ def self.validate?(email)
28
+ pattern = /\A\s*([-\p{L}\d+._]{1,64})@((?:[-\p{L}\d]+\.)+\p{L}{2,})\s*\z/i
29
+ !!(email =~ pattern)
30
+ end
31
+
32
+ # Normalize email address.
33
+ #
34
+ # You need to validate before normalize an email address.
35
+ #
36
+ # @param [String] email Email address
37
+ #
38
+ # @return [String] Email address in lowercase, without special characters.
39
+ #
40
+ # @raise [CogiEmail::NormalizationError] If can not normalize the email address.
41
+ #
42
+ # @example
43
+ # CogiEmail.normalize('(Peter Brown)<peter_brown@example.com>') # => peter_brown@example.com
44
+ # CogiEmail.normalize('peter_brown@example.com') # => peter_brown@example.com
45
+ # CogiEmail.normalize('Peter_Brown@example.com') # => peter_brown@example.com
46
+ # CogiEmail.normalize('peter brown@example.com') # => peter_brown@example.com
47
+ def self.normalize(email)
48
+ begin
49
+ m = Mail::Address.new(email)
50
+ m.address.downcase
51
+ rescue
52
+ raise CogiEmail::NormalizationError
53
+ end
54
+ end
55
+
56
+ # Check if email domain is valid by making a DNS lookup.
57
+ #
58
+ # An email domain is valid if it has an MX DNS record is set up to receive mail.
59
+ #
60
+ # Reference: https://www.safaribooksonline.com/library/view/ruby-cookbook/0596523696/ch01s19.html
61
+ #
62
+ # @param [String] email Email address
63
+ #
64
+ # @return [Boolean] True if email domain have valid MX DNS record, otherwise False
65
+ def self.valid_email_domain?(email)
66
+ valid = true
67
+
68
+ begin
69
+ m = Mail::Address.new(email)
70
+ hostname = m.domain
71
+ Resolv::DNS.new.getresource(hostname, Resolv::DNS::Resource::IN::MX)
72
+ rescue Resolv::ResolvError
73
+ valid = false
74
+ end
75
+
76
+ valid
77
+ end
78
+
79
+ # Check if an email address is real or not.
80
+ #
81
+ # An email address is real if:
82
+ # Valid
83
+ # Has MX DNS record
84
+ # Can send test email
85
+ #
86
+ # @param [String] email Email address
87
+ #
88
+ # @return [Boolean] True if email address is real, otherwise False
89
+ #
90
+ # @example
91
+ # CogiEmail.real_email?('nobi.younet@gmail.com') # => true
92
+ # CogiEmail.real_email?('nobi.younet@example.com') # => false
93
+ def self.real_email?(email)
94
+ return false unless self.validate?(email) # not a valid email address
95
+ result = true
96
+
97
+ begin
98
+ v = CogiEmail::Checker.new(email)
99
+ v.connect
100
+ v.verify
101
+ rescue
102
+ result = false
103
+ end
104
+
105
+ result
106
+ end
107
+ end
@@ -0,0 +1,108 @@
1
+ require 'net/smtp'
2
+ require 'resolv'
3
+
4
+ module CogiEmail
5
+ class Checker
6
+ ##
7
+ # Returns server object for given email address or throws exception
8
+ # Object returned isn't yet connected. It has internally a list of
9
+ # real mail servers got from MX dns lookup
10
+ #
11
+ # Reference:
12
+ # https://github.com/kamilc/email_verifier/blob/master/lib/email_verifier/checker.rb
13
+ def initialize(email)
14
+ @email = email
15
+ @smtp = nil
16
+ @user_email = 'nobody@nonexistant.com'
17
+ _, @user_domain = @user_email.split "@"
18
+ end
19
+
20
+ def domain
21
+ m = Mail::Address.new(@email)
22
+ @domain = m.domain
23
+ end
24
+
25
+ def list_mxs
26
+ return [] unless domain
27
+ mxs = []
28
+ Resolv::DNS.open do |dns|
29
+ ress = dns.getresources domain, Resolv::DNS::Resource::IN::MX
30
+ ress.each do |r|
31
+ mxs << { priority: r.preference, address: r.exchange.to_s }
32
+ end
33
+ end
34
+
35
+ @servers = mxs.sort_by { |mx| mx[:priority] }
36
+ @servers
37
+ end
38
+
39
+ def is_connected
40
+ !@smtp.nil?
41
+ end
42
+
43
+ def connect
44
+ list_mxs
45
+ raise CogiEmail::NoMailServerException.new("No mail server for #{@email}") if @servers.empty?
46
+
47
+ begin
48
+ server = next_server
49
+ raise CogiEmail::OutOfMailServersException.new("Unable to connect to any one of mail servers for #{@email}") if server.nil?
50
+ @smtp = Net::SMTP.start server[:address], 25, @user_domain
51
+ return true
52
+ rescue CogiEmail::OutOfMailServersException => e
53
+ raise CogiEmail::OutOfMailServersException, e.message
54
+ rescue => e
55
+ retry
56
+ end
57
+ end
58
+
59
+ def close_connection
60
+ @smtp.finish if @smtp && @smtp.started?
61
+ end
62
+
63
+ def verify
64
+ mailfrom @user_email
65
+ rcptto(@email).tap do
66
+ close_connection
67
+ end
68
+ end
69
+
70
+ def next_server
71
+ @servers.shift
72
+ end
73
+
74
+ private
75
+
76
+ def ensure_connected
77
+ raise CogiEmail::NotConnectedException.new("You have to connect first") if @smtp.nil?
78
+ end
79
+
80
+ def mailfrom(address)
81
+ ensure_connected
82
+
83
+ ensure_250 @smtp.mailfrom(address)
84
+ end
85
+
86
+ def rcptto(address)
87
+ ensure_connected
88
+
89
+ begin
90
+ ensure_250 @smtp.rcptto(address)
91
+ rescue => e
92
+ if e.message[/^550/]
93
+ return false
94
+ else
95
+ raise CogiEmail::FailureException.new(e.message)
96
+ end
97
+ end
98
+ end
99
+
100
+ def ensure_250(smtp_return)
101
+ if smtp_return.status.to_i == 250
102
+ return true
103
+ else
104
+ raise CogiEmail::FailureException.new "Mail server responded with #{smtp_return.status} when we were expecting 250"
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,12 @@
1
+ module CogiEmail
2
+ class NormalizationError < StandardError
3
+ def initialize
4
+ super %Q{Can not normalize the given email address. It is not an valid email address.}
5
+ end
6
+ end
7
+
8
+ class NoMailServerException < StandardError; end
9
+ class OutOfMailServersException < StandardError; end
10
+ class NotConnectedException < StandardError; end
11
+ class FailureException < StandardError; end
12
+ end
@@ -0,0 +1,3 @@
1
+ module CogiEmail
2
+ VERSION = '0.0.1'.freeze
3
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'cogi_email/checker'
3
+
4
+ describe CogiEmail::Checker do
5
+ describe '#verify' do
6
+ it 'return true if valid email address' do
7
+ v = CogiEmail::Checker.new('nobi.younet@gmail.com')
8
+ v.connect
9
+ expect(v.verify).to be_truthy
10
+ end
11
+
12
+ it 'raise error if no mail server found' do
13
+ v = CogiEmail::Checker.new('nobi.younet@example.com')
14
+ expect{ v.connect }.to raise_error(CogiEmail::NoMailServerException)
15
+ end
16
+
17
+ it 'raise error if not connected' do
18
+ v = CogiEmail::Checker.new('nobi.younet@gmail.com')
19
+ expect{ v.verify }.to raise_error(CogiEmail::NotConnectedException)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+
3
+ describe CogiEmail do
4
+ subject { CogiEmail.new }
5
+
6
+ VALID_EMAIL_ADDRESSES = [
7
+ "a+b@plus-in-local.com",
8
+ "a_b@underscore-in-local.com",
9
+ "user@example.com",
10
+ " user@example.com ",
11
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@letters-in-local.org",
12
+ "01234567890@numbers-in-local.net",
13
+ "a@single-character-in-local.org",
14
+ "one-character-third-level@a.example.com",
15
+ "single-character-in-sld@x.org",
16
+ "local@dash-in-sld.com",
17
+ "letters-in-sld@123.com",
18
+ "one-letter-sld@x.org",
19
+ "uncommon-tld@sld.museum",
20
+ "uncommon-tld@sld.travel",
21
+ "uncommon-tld@sld.mobi",
22
+ "country-code-tld@sld.uk",
23
+ "country-code-tld@sld.rw",
24
+ "local@sld.newTLD",
25
+ "local@sub.domains.com",
26
+ "aaa@bbb.co.jp",
27
+ "nigel.worthington@big.co.uk",
28
+ "f@c.com",
29
+ "areallylongnameaasdfasdfasdfasdf@asdfasdfasdfasdfasdf.ab.cd.ef.gh.co.ca",
30
+ "joe+ruby-mail@example.com"
31
+ ]
32
+
33
+ INVALID_EMAIL_ADDRESSES = [
34
+ "",
35
+ "f@s",
36
+ "f@s.c",
37
+ "@bar.com",
38
+ "test@example.com@example.com",
39
+ "test@",
40
+ "@missing-local.org",
41
+ "a b@space-in-local.com",
42
+ "! \#$%\`|@invalid-characters-in-local.org",
43
+ "<>@[]\`|@even-more-invalid-characters-in-local.org",
44
+ "missing-sld@.com",
45
+ "invalid-characters-in-sld@! \"\#$%(),/;<>_[]\`|.org",
46
+ "missing-dot-before-tld@com",
47
+ "missing-tld@sld.",
48
+ " ",
49
+ "missing-at-sign.net",
50
+ "unbracketed-IP@127.0.0.1",
51
+ "invalid-ip@127.0.0.1.26",
52
+ "another-invalid-ip@127.0.0.256",
53
+ "IP-and-port@127.0.0.1:25",
54
+ "the-local-part-is-invalid-if-it-is-longer-than-sixty-four-characters@sld.net",
55
+ "user@example.com\n<script>alert('hello')</script>"
56
+ ]
57
+
58
+ describe '#validate?' do
59
+ context 'valid email address' do
60
+ VALID_EMAIL_ADDRESSES.each do |email|
61
+ it "#{email.inspect} should be valid" do
62
+ expect(CogiEmail.validate?(email)).to be_truthy
63
+ end
64
+ end
65
+ end
66
+
67
+ context 'invalid email address' do
68
+ INVALID_EMAIL_ADDRESSES.each do |email|
69
+ it "#{email.inspect} should not be valid" do
70
+ expect(CogiEmail.validate?(email)).to be_falsey
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ describe '#normalize' do
77
+ context 'valid email address' do
78
+ VALID_EMAIL_ADDRESSES.each do |email|
79
+ it "#{email.inspect} return an valid email address" do
80
+ expect(CogiEmail.normalize(email)).to eq(email.strip.downcase)
81
+ end
82
+ end
83
+
84
+ [
85
+ "",
86
+ "test@",
87
+ "@missing-local.org",
88
+ "a b@space-in-local.com",
89
+ "! \#$%\`|@invalid-characters-in-local.org",
90
+ "<>@[]\`|@even-more-invalid-characters-in-local.org",
91
+ "invalid-characters-in-sld@! \"\#$%(),/;<>_[]\`|.org",
92
+ "missing-tld@sld.",
93
+ " ",
94
+ "IP-and-port@127.0.0.1:25",
95
+ "user@example.com\n<script>alert('hello')</script>"
96
+ ].each do |email|
97
+ it "#{email.inspect} raise error" do
98
+ expect{ CogiEmail.normalize(email) }.to raise_error(CogiEmail::NormalizationError)
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ describe '#valid_email_domain?' do
105
+ context 'Wellknown email providers' do
106
+ %w(gmail.com hotmail.com outlook.com yahoo.com aol.com).each do |domain|
107
+ it "peter.brown@#{domain} is valid" do
108
+ expect(CogiEmail.valid_email_domain?("peter.brown@#{domain}")).to be_truthy
109
+ end
110
+ end
111
+ end
112
+
113
+ context 'Email domain do not have MX record' do
114
+ %w(example.com aabbbccddee.example localhost).each do |domain|
115
+ it "peter.brown@#{domain} is not valid" do
116
+ expect(CogiEmail.valid_email_domain?("peter.brown@#{domain}")).to be_falsey
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ describe '#real_email?' do
123
+ context 'real email' do
124
+ it 'return true' do
125
+ expect(CogiEmail.real_email?('nobi.younet@gmail.com')).to be_truthy
126
+ end
127
+ end
128
+
129
+ context 'fake email' do
130
+ it 'return false' do
131
+ expect(CogiEmail.real_email?('nobi.younet@example.com')).to be_falsey
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1 @@
1
+ require 'cogi_email'
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cogi_email
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nobi Hoang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mail
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.5'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.5'
69
+ description: This gem provide you library to validate, parsing and format email. Check
70
+ is email is in blacklist or not.
71
+ email: nobi.hoa@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - lib/cogi_email.rb
80
+ - lib/cogi_email/checker.rb
81
+ - lib/cogi_email/error.rb
82
+ - lib/cogi_email/version.rb
83
+ - spec/cogi_email/checker_spec.rb
84
+ - spec/cogi_email_spec.rb
85
+ - spec/spec_helper.rb
86
+ homepage: https://github.com/hoahm/cogi_email
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options:
92
+ - "--title"
93
+ - Rake -- Ruby Make
94
+ - "--main"
95
+ - README
96
+ - "--line-numbers"
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 2.0.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.5.1
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: This gem provide you library to validate, parsing and format email.
115
+ test_files:
116
+ - spec/cogi_email/checker_spec.rb
117
+ - spec/cogi_email_spec.rb
118
+ - spec/spec_helper.rb