Module: ValidatesEmailFormatOf

Defined in:
lib/validates_email_format_of.rb

Defined Under Namespace

Modules: Validations

Constant Summary collapse

VERSION =
'1.5.1'
MessageScope =
defined?(ActiveModel) ? :activemodel : :activerecord
LocalPartSpecialChars =
/[\!\#\$\%\&\'\*\-\/\=\?\+\-\^\_\`\{\|\}\~]/

Class Method Summary collapse

Class Method Details

.validate_domain_part_syntax(domain) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/validates_email_format_of.rb', line 112

def self.validate_domain_part_syntax(domain)
  parts = domain.downcase.split('.', -1)
  
  return false if parts.length <= 1 # Only one domain part

  # Empty parts (double period) or invalid chars
  return false if parts.any? { 
    |part| 
      part.nil? or 
      part.empty? or 
      not part =~ /\A[[:alnum:]\-]+\Z/ or
      part[0,1] == '-' or part[-1,1] == '-' # hyphen at beginning or end of part
  } 
      
  # ipv4
  return true if parts.length == 4 and parts.all? { |part| part =~ /\A[0-9]+\Z/ and part.to_i.between?(0, 255) }
      
  return false if parts[-1].length < 2 or not parts[-1] =~ /[a-z\-]/ # TLD is too short or does not contain a char or hyphen
  
  return true
end

.validate_email_domain(email) ⇒ Object



11
12
13
14
15
16
17
# File 'lib/validates_email_format_of.rb', line 11

def self.validate_email_domain(email)
  domain = email.match(/\@(.+)/)[1]
  Resolv::DNS.open do |dns|
    @mx = dns.getresources(domain, Resolv::DNS::Resource::IN::MX) + dns.getresources(domain, Resolv::DNS::Resource::IN::A)
  end
  @mx.size > 0 ? true : false
end

.validate_email_format(email, options = {}) ⇒ Object

Validates whether the specified value is a valid email address. Returns nil if the value is valid, otherwise returns an array containing one or more validation error messages.

Configuration options:

  • message - A custom error message (default is: “does not appear to be valid”)

  • check_mx - Check for MX records (default is false)

  • mx_message - A custom error message when an MX record validation fails (default is: “is not routable.”)

  • with The regex to use for validating the format of the email address (deprecated)

  • local_length Maximum number of characters allowed in the local part (default is 64)

  • domain_length Maximum number of characters allowed in the domain part (default is 255)



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/validates_email_format_of.rb', line 29

def self.validate_email_format(email, options={})
    default_options = { :message => I18n.t(:invalid_email_address, :scope => [MessageScope, :errors, :messages], :default => 'does not appear to be valid'),
                        :check_mx => false,
                        :mx_message => I18n.t(:email_address_not_routable, :scope => [MessageScope, :errors, :messages], :default => 'is not routable'),
                        :domain_length => 255,
                        :local_length => 64
                        }
    opts = options.merge(default_options) {|key, old, new| old}  # merge the default options into the specified options, retaining all specified options

    email = email.strip if email

    begin
      domain, local = email.reverse.split('@', 2)
    rescue
      return [ opts[:message] ]
    end

    # need local and domain parts
    return [ opts[:message] ] unless local and domain

    # check lengths
    return [ opts[:message] ] unless domain.length <= opts[:domain_length] and local.length <= opts[:local_length]

    local.reverse!
    domain.reverse!

    if opts.has_key?(:with) # holdover from versions <= 1.4.7
      return [ opts[:message] ] unless email =~ opts[:with]
    else
      return [ opts[:message] ] unless self.validate_local_part_syntax(local) and self.validate_domain_part_syntax(domain)
    end

    if opts[:check_mx] and !self.validate_email_domain(email)
      return [ opts[:mx_message] ]
    end

    return nil    # represents no validation errors
end

.validate_local_part_syntax(local) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/validates_email_format_of.rb', line 69

def self.validate_local_part_syntax(local)
  in_quoted_pair = false
  in_quoted_string = false

  (0..local.length-1).each do |i|
    ord = local[i].ord

    # accept anything if it's got a backslash before it
    if in_quoted_pair 
      in_quoted_pair = false
      next
    end
      
    # backslash signifies the start of a quoted pair
    if ord == 92 and i < local.length - 1
      return false if not in_quoted_string # must be in quoted string per http://www.rfc-editor.org/errata_search.php?rfc=3696
      in_quoted_pair = true
      next
    end
      
    # double quote delimits quoted strings
    if ord == 34
      in_quoted_string = !in_quoted_string
      next
    end    

    next if local[i,1] =~ /[[:alnum:]]/
    next if local[i,1] =~ LocalPartSpecialChars
    
    # period must be followed by something
    if ord == 46
      return false if i == 0 or i == local.length - 1 # can't be first or last char
      next unless local[i+1].ord == 46 # can't be followed by a period
    end

    return false
  end
  
  return false if in_quoted_string # unbalanced quotes

  return true
end