File: validate_email.rb

package info (click to toggle)
ruby-valid-email 0.1.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 280 kB
  • sloc: ruby: 575; makefile: 4
file content (132 lines) | stat: -rw-r--r-- 4,180 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
67
68
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
require 'mail'
require 'simpleidn'

class ValidateEmail
  class << self
    SPECIAL_CHARS = %w(( ) , : ; < > @ [ ])
    SPECIAL_ESCAPED_CHARS = %w(\  \\ ")
    LOCAL_MAX_LEN = 64
    DOMAIN_REGEX = /\A(?:[[:alnum:]](?:[[[:alnum:]]-]{0,61}[[:alnum:]])?)(?:\.(?:[[:alnum:]](?:[[[:alnum:]]-]{0,61}[[:alnum:]])?))+\z/

    def valid?(value, user_options={})
      options = {
        :mx => false,
        :domain => true,
        :message => nil
      }.merge(user_options)

      m = Mail::Address.new(value)
      # We must check that value contains a domain and that value is an email address
      return false unless m.domain && m.address == value

      # Check that domain consists of dot-atom-text elements > 1 and does not
      # contain spaces.
      #
      # In mail 2.6.1, domains are invalid per rfc2822 are parsed when they shouldn't
      # This is to make sure we cover those cases

      return false unless m.domain.match(/^\S+$/)

      domain_dot_elements = m.domain.split(/\./)
      return false unless domain_dot_elements.size > 1 && domain_dot_elements.none?(&:empty?)

      # Ensure that the local segment adheres to adheres to RFC-5322
      return false unless valid_local?(m.local)

      # Check if domain has DNS MX record
      if options[:mx]
        require 'valid_email/mx_validator'
        return mx_valid?(value)
      end

      if options[:domain]
        require 'valid_email/domain_validator'
        return domain_valid?(value)
      end

      true
    rescue Mail::Field::ParseError
      false
    rescue ArgumentError => error
      if error.message == 'bad value for range'
        false
      else
        raise error
      end
    end

    def valid_local?(local)
      return false unless local.length <= LOCAL_MAX_LEN
      # Emails can be validated by segments delineated by '.', referred to as dot atoms.
      # See http://tools.ietf.org/html/rfc5322#section-3.2.3
      return local.split('.', -1).all? { |da| valid_dot_atom?(da) }
    end

    def valid_dot_atom?(dot_atom)
      # Leading, trailing, and double '.'s aren't allowed
      return false if dot_atom.empty?
      # A dot atom can be quoted, which allows use of the SPECIAL_CHARS
      if dot_atom[0] == '"' || dot_atom[-1] == '"'
        # A quoted segment must have leading and trailing '"#"'s
        return false if dot_atom[0] != dot_atom[-1]

        # Excluding the bounding quotes, all of the SPECIAL_ESCAPED_CHARS must have a leading '\'
        index = dot_atom.length - 2
        while index > 0
          if SPECIAL_ESCAPED_CHARS.include? dot_atom[index]
            return false if index == 1 || dot_atom[index - 1] != '\\'
            # On an escaped special character, skip an index to ignore the '\' that's doing the escaping
            index -= 1
          end
          index -= 1
        end
      else
        # If we're not in a quoted dot atom then no special characters are allowed.
        return false unless ((SPECIAL_CHARS | SPECIAL_ESCAPED_CHARS) & dot_atom.split('')).empty?
      end
      return true
    end

    def mx_valid?(value, fallback=false)
      m = Mail::Address.new(value)
      return false unless m.domain

      if m.domain.ascii_only?
        ascii_domain = m.domain
      else
        ascii_domain = SimpleIDN.to_ascii(m.domain)
      end

      Resolv::DNS.open do |dns|
        dns.timeouts = MxValidator.config[:timeouts] unless MxValidator.config[:timeouts].empty?

        return dns.getresources(ascii_domain, Resolv::DNS::Resource::IN::MX).size > 0 || (
          fallback && dns.getresources(ascii_domain, Resolv::DNS::Resource::IN::A).size > 0
        )
      end
    rescue Mail::Field::ParseError
      false
    end

    def mx_valid_with_fallback?(value)
      mx_valid?(value, true)
    end

    def domain_valid?(value)
      m = Mail::Address.new(value)
      return false unless m.domain

      !(m.domain =~ DOMAIN_REGEX).nil?

    rescue Mail::Field::ParseError
      false
    end

    def ban_disposable_email?(value)
      m = Mail::Address.new(value)
      m.domain && !BanDisposableEmailValidator.config.include?(m.domain)
    rescue Mail::Field::ParseError
      false
    end
  end
end