cogi_email 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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