email_address 0.1.18 → 0.2.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 +4 -4
- data/.github/workflows/ci.yml +18 -0
- data/Gemfile +1 -1
- data/README.md +27 -5
- data/Rakefile +2 -2
- data/email_address.gemspec +22 -23
- data/lib/email_address/active_record_validator.rb +7 -10
- data/lib/email_address/address.rb +25 -19
- data/lib/email_address/config.rb +13 -2
- data/lib/email_address/exchanger.rb +5 -19
- data/lib/email_address/host.rb +21 -38
- data/lib/email_address/local.rb +104 -105
- data/lib/email_address/rewriter.rb +28 -31
- data/lib/email_address/version.rb +1 -1
- data/lib/email_address.rb +8 -9
- data/test/activerecord/test_ar.rb +17 -13
- data/test/activerecord/user.rb +31 -30
- data/test/email_address/test_address.rb +44 -25
- data/test/email_address/test_config.rb +8 -8
- data/test/email_address/test_exchanger.rb +6 -7
- data/test/email_address/test_host.rb +2 -1
- data/test/email_address/test_local.rb +39 -35
- data/test/email_address/test_rewriter.rb +2 -5
- data/test/test_aliasing.rb +1 -2
- data/test/test_email_address.rb +14 -18
- data/test/test_helper.rb +9 -8
- metadata +29 -18
- data/.travis.yml +0 -9
data/lib/email_address/host.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require "simpleidn"
|
2
2
|
require "resolv"
|
3
|
-
require "netaddr"
|
4
3
|
require "net/smtp"
|
5
4
|
|
6
5
|
module EmailAddress
|
@@ -34,15 +33,15 @@ module EmailAddress
|
|
34
33
|
attr_reader :host_name
|
35
34
|
attr_accessor :dns_name, :domain_name, :registration_name,
|
36
35
|
:tld, :tld2, :subdomains, :ip_address, :config, :provider,
|
37
|
-
:comment, :error_message, :reason
|
36
|
+
:comment, :error_message, :reason, :locale
|
38
37
|
MAX_HOST_LENGTH = 255
|
39
38
|
|
40
39
|
# Sometimes, you just need a Regexp...
|
41
|
-
DNS_HOST_REGEX = / [\p{L}\p{N}]+ (?: (?:
|
40
|
+
DNS_HOST_REGEX = / [\p{L}\p{N}]+ (?: (?: -{1,2} | \.) [\p{L}\p{N}]+ )*/x
|
42
41
|
|
43
42
|
# The IPv4 and IPv6 were lifted from Resolv::IPv?::Regex and tweaked to not
|
44
43
|
# \A...\z anchor at the edges.
|
45
|
-
|
44
|
+
IPV6_HOST_REGEX = /\[IPv6:
|
46
45
|
(?: (?:(?x-mi:
|
47
46
|
(?:[0-9A-Fa-f]{1,4}:){7}
|
48
47
|
[0-9A-Fa-f]{1,4}
|
@@ -61,7 +60,7 @@ module EmailAddress
|
|
61
60
|
(?: \d+)\.(?: \d+)\.(?: \d+)\.(?: \d+)
|
62
61
|
)))\]/ix
|
63
62
|
|
64
|
-
|
63
|
+
IPV4_HOST_REGEX = /\[((?x-mi:0
|
65
64
|
|1(?:[0-9][0-9]?)?
|
66
65
|
|2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
|
67
66
|
|[3-9][0-9]?))\.((?x-mi:0
|
@@ -80,12 +79,13 @@ module EmailAddress
|
|
80
79
|
|
81
80
|
# Matches Host forms: DNS name, IPv4, or IPv6 formats
|
82
81
|
STANDARD_HOST_REGEX = /\A (?: #{DNS_HOST_REGEX}
|
83
|
-
| #{
|
82
|
+
| #{IPV4_HOST_REGEX} | #{IPV6_HOST_REGEX}) \z/ix
|
84
83
|
|
85
84
|
# host name -
|
86
85
|
# * host type - :email for an email host, :mx for exchanger host
|
87
|
-
def initialize(host_name, config = {})
|
86
|
+
def initialize(host_name, config = {}, locale = "en")
|
88
87
|
@original = host_name ||= ""
|
88
|
+
@locale = locale
|
89
89
|
config[:host_type] ||= :email
|
90
90
|
@config = config.is_a?(Hash) ? Config.new(config) : config
|
91
91
|
@error = @error_message = nil
|
@@ -104,7 +104,7 @@ module EmailAddress
|
|
104
104
|
dns_name
|
105
105
|
end
|
106
106
|
end
|
107
|
-
|
107
|
+
alias_method :to_s, :name
|
108
108
|
|
109
109
|
# The canonical host name is the simplified, DNS host name
|
110
110
|
def canonical
|
@@ -149,7 +149,7 @@ module EmailAddress
|
|
149
149
|
if @config[:host_remove_spaces]
|
150
150
|
@host_name = @host_name.delete(" ")
|
151
151
|
end
|
152
|
-
@dns_name = if /[^[:ascii:]]/.match(host_name)
|
152
|
+
@dns_name = if /[^[:ascii:]]/.match?(host_name)
|
153
153
|
::SimpleIDN.to_ascii(host_name)
|
154
154
|
else
|
155
155
|
host_name
|
@@ -229,7 +229,7 @@ module EmailAddress
|
|
229
229
|
def parts
|
230
230
|
{host_name: host_name, dns_name: dns_name, subdomain: subdomains,
|
231
231
|
registration_name: registration_name, domain_name: domain_name,
|
232
|
-
tld2: tld2, tld: tld, ip_address: ip_address
|
232
|
+
tld2: tld2, tld: tld, ip_address: ip_address}
|
233
233
|
end
|
234
234
|
|
235
235
|
def hosted_provider
|
@@ -246,7 +246,7 @@ module EmailAddress
|
|
246
246
|
end
|
247
247
|
|
248
248
|
def ip?
|
249
|
-
ip_address
|
249
|
+
!!ip_address
|
250
250
|
end
|
251
251
|
|
252
252
|
def ipv4?
|
@@ -290,7 +290,7 @@ module EmailAddress
|
|
290
290
|
# Does "sub.example.com" match ".com" and ".example.com" top level names?
|
291
291
|
# Matches TLD (uk) or TLD2 (co.uk)
|
292
292
|
def tld_matches?(rule)
|
293
|
-
rule.match(/\A\.(.+)\z/) && ($1 == tld || $1 == tld2) ? true : false
|
293
|
+
rule.match(/\A\.(.+)\z/) && ($1 == tld || $1 == tld2) # ? true : false
|
294
294
|
end
|
295
295
|
|
296
296
|
def provider_matches?(rule)
|
@@ -310,13 +310,8 @@ module EmailAddress
|
|
310
310
|
# the passed CIDR string ("10.9.8.0/24" or "2001:..../64")
|
311
311
|
def ip_matches?(cidr)
|
312
312
|
return false unless ip_address
|
313
|
-
|
314
|
-
|
315
|
-
return cidr if NetAddr::IPv6Net.parse(cidr).contains(NetAddr::IPv6.parse(ip_address))
|
316
|
-
elsif cidr.include?(".") && ip_address.include?(".")
|
317
|
-
return cidr if NetAddr::IPv4Net.parse(cidr).contains(NetAddr::IPv4.parse(ip_address))
|
318
|
-
end
|
319
|
-
false
|
313
|
+
net = IPAddr.new(cidr)
|
314
|
+
net.include?(IPAddr.new(ip_address))
|
320
315
|
end
|
321
316
|
|
322
317
|
############################################################################
|
@@ -333,7 +328,7 @@ module EmailAddress
|
|
333
328
|
# Returns: [official_hostname, alias_hostnames, address_family, *address_list]
|
334
329
|
def dns_a_record
|
335
330
|
@_dns_a_record = "0.0.0.0" if @config[:dns_lookup] == :off
|
336
|
-
@_dns_a_record ||=
|
331
|
+
@_dns_a_record ||= Addrinfo.getaddrinfo(dns_name, 80) # Port 80 for A rec, 25 for MX
|
337
332
|
rescue SocketError # not found, but could also mean network not work
|
338
333
|
@_dns_a_record ||= []
|
339
334
|
end
|
@@ -353,8 +348,8 @@ module EmailAddress
|
|
353
348
|
records = begin
|
354
349
|
dns.getresources(alternate_host || dns_name,
|
355
350
|
Resolv::DNS::Resource::IN::TXT)
|
356
|
-
|
357
|
-
|
351
|
+
rescue Resolv::ResolvTimeout
|
352
|
+
[]
|
358
353
|
end
|
359
354
|
|
360
355
|
records.empty? ? nil : records.map(&:data).join(" ")
|
@@ -459,21 +454,9 @@ module EmailAddress
|
|
459
454
|
end
|
460
455
|
|
461
456
|
def localhost?
|
462
|
-
if
|
463
|
-
|
464
|
-
|
465
|
-
NetAddr::IPv6Net.parse("" + "::1").rel(
|
466
|
-
NetAddr::IPv6Net.parse(ip_address)
|
467
|
-
)
|
468
|
-
else
|
469
|
-
NetAddr::IPv4Net.parse("" + "127.0.0.0/8").rel(
|
470
|
-
NetAddr::IPv4Net.parse(ip_address)
|
471
|
-
)
|
472
|
-
end
|
473
|
-
!rel.nil? && rel >= 0
|
474
|
-
else
|
475
|
-
host_name == "localhost"
|
476
|
-
end
|
457
|
+
return true if host_name == "localhost"
|
458
|
+
return false unless ip_address
|
459
|
+
IPAddr.new(ip_address).loopback?
|
477
460
|
end
|
478
461
|
|
479
462
|
# Connects to host to test it can receive email. This should NOT be performed
|
@@ -497,7 +480,7 @@ module EmailAddress
|
|
497
480
|
def set_error(err, reason = nil)
|
498
481
|
@error = err
|
499
482
|
@reason = reason
|
500
|
-
@error_message = Config.error_message(err)
|
483
|
+
@error_message = Config.error_message(err, locale)
|
501
484
|
false
|
502
485
|
end
|
503
486
|
|
data/lib/email_address/local.rb
CHANGED
@@ -67,50 +67,51 @@ module EmailAddress
|
|
67
67
|
# [CFWS]
|
68
68
|
############################################################################
|
69
69
|
class Local
|
70
|
-
attr_reader
|
70
|
+
attr_reader :local
|
71
71
|
attr_accessor :mailbox, :comment, :tag, :config, :original
|
72
|
-
attr_accessor :syntax
|
72
|
+
attr_accessor :syntax, :locale
|
73
73
|
|
74
74
|
# RFC-2142: MAILBOX NAMES FOR COMMON SERVICES, ROLES AND FUNCTIONS
|
75
|
-
BUSINESS_MAILBOXES = %w
|
76
|
-
NETWORK_MAILBOXES
|
77
|
-
SERVICE_MAILBOXES
|
78
|
-
SYSTEM_MAILBOXES
|
79
|
-
ROLE_MAILBOXES
|
80
|
-
SPECIAL_MAILBOXES
|
81
|
-
|
82
|
-
STANDARD_MAX_SIZE
|
75
|
+
BUSINESS_MAILBOXES = %w[info marketing sales support]
|
76
|
+
NETWORK_MAILBOXES = %w[abuse noc security]
|
77
|
+
SERVICE_MAILBOXES = %w[postmaster hostmaster usenet news webmaster www uucp ftp]
|
78
|
+
SYSTEM_MAILBOXES = %w[help mailer-daemon root] # Not from RFC-2142
|
79
|
+
ROLE_MAILBOXES = %w[staff office orders billing careers jobs] # Not from RFC-2142
|
80
|
+
SPECIAL_MAILBOXES = BUSINESS_MAILBOXES + NETWORK_MAILBOXES + SERVICE_MAILBOXES +
|
81
|
+
SYSTEM_MAILBOXES + ROLE_MAILBOXES
|
82
|
+
STANDARD_MAX_SIZE = 64
|
83
83
|
|
84
84
|
# Conventional : word([.-+'_]word)*
|
85
|
-
CONVENTIONAL_MAILBOX_REGEX
|
86
|
-
CONVENTIONAL_MAILBOX_WITHIN = /[\p{L}\p{N}_]+ (?: [
|
85
|
+
CONVENTIONAL_MAILBOX_REGEX = /\A [\p{L}\p{N}_]+ (?: [.\-+'_] [\p{L}\p{N}_]+ )* \z/x
|
86
|
+
CONVENTIONAL_MAILBOX_WITHIN = /[\p{L}\p{N}_]+ (?: [.\-+'_] [\p{L}\p{N}_]+ )*/x
|
87
87
|
|
88
88
|
# Relaxed: same characters, relaxed order
|
89
|
-
RELAXED_MAILBOX_WITHIN = /[\p{L}\p{N}_]+ (?: [
|
90
|
-
RELAXED_MAILBOX_REGEX = /\A [\p{L}\p{N}_]+ (?: [
|
89
|
+
RELAXED_MAILBOX_WITHIN = /[\p{L}\p{N}_]+ (?: [.\-+'_]+ [\p{L}\p{N}_]+ )*/x
|
90
|
+
RELAXED_MAILBOX_REGEX = /\A [\p{L}\p{N}_]+ (?: [.\-+'_]+ [\p{L}\p{N}_]+ )* \z/x
|
91
91
|
|
92
92
|
# RFC5322 Token: token."token".token (dot-separated tokens)
|
93
93
|
# Quoted Token can also have: SPACE \" \\ ( ) , : ; < > @ [ \ ] .
|
94
94
|
STANDARD_LOCAL_WITHIN = /
|
95
|
-
(?: [\p{L}\p{N}
|
96
|
-
|
|
97
|
-
(?: \. (?: [\p{L}\p{N}
|
98
|
-
|
|
95
|
+
(?: [\p{L}\p{N}!\#$%&'*+\-\/=?\^_`{|}~()]+
|
96
|
+
| " (?: \\[" \\] | [\x20-\x21\x23-\x2F\x3A-\x40\x5B\x5D-\x60\x7B-\x7E\p{L}\p{N}] )+ " )
|
97
|
+
(?: \. (?: [\p{L}\p{N}!\#$%&'*+\-\/=?\^_`{|}~()]+
|
98
|
+
| " (?: \\[" \\] | [\x20-\x21\x23-\x2F\x3A-\x40\x5B\x5D-\x60\x7B-\x7E\p{L}\p{N}] )+ " ) )* /x
|
99
99
|
|
100
100
|
STANDARD_LOCAL_REGEX = /\A #{STANDARD_LOCAL_WITHIN} \z/x
|
101
101
|
|
102
102
|
REDACTED_REGEX = /\A \{ [0-9a-f]{40} \} \z/x # {sha1}
|
103
103
|
|
104
|
-
CONVENTIONAL_TAG_REGEX
|
105
|
-
%r
|
106
|
-
RELAXED_TAG_REGEX
|
107
|
-
%r/^([\w
|
104
|
+
CONVENTIONAL_TAG_REGEX = # AZaz09_!'+-/=
|
105
|
+
%r{^([\w!'+\-/=.]+)$}i
|
106
|
+
RELAXED_TAG_REGEX = # AZaz09_!#$%&'*+-/=?^`{|}~
|
107
|
+
%r/^([\w.!\#$%&'*+\-\/=?\^`{|}~]+)$/i
|
108
108
|
|
109
|
-
def initialize(local, config={}, host=nil)
|
109
|
+
def initialize(local, config = {}, host = nil, locale = "en")
|
110
110
|
@config = config.is_a?(Hash) ? Config.new(config) : config
|
111
|
-
self.local
|
112
|
-
@host
|
113
|
-
@
|
111
|
+
self.local = local
|
112
|
+
@host = host
|
113
|
+
@locale = locale
|
114
|
+
@error = @error_message = nil
|
114
115
|
end
|
115
116
|
|
116
117
|
def local=(raw)
|
@@ -121,23 +122,23 @@ module EmailAddress
|
|
121
122
|
if @config[:local_parse].is_a?(Proc)
|
122
123
|
self.mailbox, self.tag, self.comment = @config[:local_parse].call(raw)
|
123
124
|
else
|
124
|
-
self.mailbox, self.tag, self.comment =
|
125
|
+
self.mailbox, self.tag, self.comment = parse(raw)
|
125
126
|
end
|
126
127
|
|
127
128
|
self.format
|
128
129
|
end
|
129
130
|
|
130
131
|
def parse(raw)
|
131
|
-
if raw =~ /\A
|
132
|
+
if raw =~ /\A"(.*)"\z/ # Quoted
|
132
133
|
raw = $1
|
133
134
|
raw = raw.gsub(/\\(.)/, '\1') # Unescape
|
134
135
|
elsif @config[:local_fix] && @config[:local_format] != :standard
|
135
|
-
raw = raw.
|
136
|
-
raw = raw.
|
137
|
-
#raw.gsub!(/([^\p{L}\p{N}]{2,10})/) {|s| s[0] } # Stutter punctuation typo
|
136
|
+
raw = raw.delete(" ")
|
137
|
+
raw = raw.tr(",", ".")
|
138
|
+
# raw.gsub!(/([^\p{L}\p{N}]{2,10})/) {|s| s[0] } # Stutter punctuation typo
|
138
139
|
end
|
139
|
-
raw, comment =
|
140
|
-
mailbox, tag =
|
140
|
+
raw, comment = parse_comment(raw)
|
141
|
+
mailbox, tag = parse_tag(raw)
|
141
142
|
mailbox ||= ""
|
142
143
|
[mailbox, tag, comment]
|
143
144
|
end
|
@@ -156,28 +157,28 @@ module EmailAddress
|
|
156
157
|
end
|
157
158
|
|
158
159
|
def parse_tag(raw)
|
159
|
-
separator = @config[:tag_separator] ||=
|
160
|
+
separator = @config[:tag_separator] ||= "+"
|
160
161
|
raw.split(separator, 2)
|
161
162
|
end
|
162
163
|
|
163
164
|
# True if the the value contains only Latin characters (7-bit ASCII)
|
164
165
|
def ascii?
|
165
|
-
!
|
166
|
+
!unicode?
|
166
167
|
end
|
167
168
|
|
168
169
|
# True if the the value contains non-Latin Unicde characters
|
169
170
|
def unicode?
|
170
|
-
|
171
|
+
/[^\p{InBasicLatin}]/.match?(local)
|
171
172
|
end
|
172
173
|
|
173
174
|
# Returns true if the value matches the Redacted format
|
174
175
|
def redacted?
|
175
|
-
|
176
|
+
REDACTED_REGEX.match?(local)
|
176
177
|
end
|
177
178
|
|
178
179
|
# Returns true if the value matches the Redacted format
|
179
180
|
def self.redacted?(local)
|
180
|
-
|
181
|
+
REDACTED_REGEX.match?(local)
|
181
182
|
end
|
182
183
|
|
183
184
|
# Is the address for a common system or business role account?
|
@@ -190,81 +191,80 @@ module EmailAddress
|
|
190
191
|
end
|
191
192
|
|
192
193
|
# Builds the local string according to configurations
|
193
|
-
def format(form
|
194
|
+
def format(form = @config[:local_format] || :conventional)
|
194
195
|
if @config[:local_format].is_a?(Proc)
|
195
196
|
@config[:local_format].call(self)
|
196
197
|
elsif form == :conventional
|
197
|
-
|
198
|
+
conventional
|
198
199
|
elsif form == :canonical
|
199
|
-
|
200
|
+
canonical
|
200
201
|
elsif form == :relaxed
|
201
|
-
|
202
|
+
relax
|
202
203
|
elsif form == :standard
|
203
|
-
|
204
|
+
standard
|
204
205
|
end
|
205
206
|
end
|
206
207
|
|
207
208
|
# Returns a conventional form of the address
|
208
209
|
def conventional
|
209
|
-
if
|
210
|
-
[
|
210
|
+
if tag
|
211
|
+
[mailbox, tag].join(@config[:tag_separator])
|
211
212
|
else
|
212
|
-
|
213
|
+
mailbox
|
213
214
|
end
|
214
215
|
end
|
215
216
|
|
216
217
|
# Returns a canonical form of the address
|
217
218
|
def canonical
|
218
219
|
if @config[:mailbox_canonical]
|
219
|
-
@config[:mailbox_canonical].call(
|
220
|
+
@config[:mailbox_canonical].call(mailbox)
|
220
221
|
else
|
221
|
-
|
222
|
+
mailbox.downcase
|
222
223
|
end
|
223
224
|
end
|
224
225
|
|
225
226
|
# Relaxed format: mailbox and tag, no comment, no extended character set
|
226
227
|
def relax
|
227
|
-
form =
|
228
|
-
form += @config[:tag_separator] +
|
229
|
-
form
|
230
|
-
form
|
228
|
+
form = mailbox
|
229
|
+
form += @config[:tag_separator] + tag if tag
|
230
|
+
form.gsub(/[ "(),:<>@\[\]\\]/, "")
|
231
231
|
end
|
232
232
|
|
233
233
|
# Returns a normalized version of the standard address parts.
|
234
234
|
def standard
|
235
|
-
form =
|
236
|
-
form += @config[:tag_separator] +
|
237
|
-
form += "(" +
|
238
|
-
form = form.gsub(/([
|
239
|
-
if
|
240
|
-
form = %
|
235
|
+
form = mailbox
|
236
|
+
form += @config[:tag_separator] + tag if tag
|
237
|
+
form += "(" + comment + ")" if comment
|
238
|
+
form = form.gsub(/([\\"])/, '\\\1') # Escape \ and "
|
239
|
+
if /[ "(),:<>@\[\\\]]/.match?(form) # Space and "(),:;<>@[\]
|
240
|
+
form = %("#{form}")
|
241
241
|
end
|
242
242
|
form
|
243
243
|
end
|
244
244
|
|
245
245
|
# Sets the part to be the conventional form
|
246
246
|
def conventional!
|
247
|
-
self.local =
|
247
|
+
self.local = conventional
|
248
248
|
end
|
249
249
|
|
250
250
|
# Sets the part to be the canonical form
|
251
251
|
def canonical!
|
252
|
-
self.local =
|
252
|
+
self.local = canonical
|
253
253
|
end
|
254
254
|
|
255
255
|
# Dropps unusual parts of Standard form to form a relaxed version.
|
256
256
|
def relax!
|
257
|
-
self.local =
|
257
|
+
self.local = relax
|
258
258
|
end
|
259
259
|
|
260
260
|
# Returns the munged form of the address, like "ma*****"
|
261
261
|
def munge
|
262
|
-
|
262
|
+
to_s.sub(/\A(.{1,2}).*/) { |m| $1 + @config[:munge_string] }
|
263
263
|
end
|
264
264
|
|
265
265
|
# Mailbox with trailing numbers removed
|
266
266
|
def root_name
|
267
|
-
|
267
|
+
mailbox =~ /\A(.+?)\d+\z/ ? $1 : mailbox
|
268
268
|
end
|
269
269
|
|
270
270
|
############################################################################
|
@@ -272,19 +272,19 @@ module EmailAddress
|
|
272
272
|
############################################################################
|
273
273
|
|
274
274
|
# True if the part is valid according to the configurations
|
275
|
-
def valid?(format
|
275
|
+
def valid?(format = @config[:local_format] || :conventional)
|
276
276
|
if @config[:mailbox_validator].is_a?(Proc)
|
277
|
-
@config[:mailbox_validator].call(
|
277
|
+
@config[:mailbox_validator].call(mailbox, tag)
|
278
278
|
elsif format.is_a?(Proc)
|
279
279
|
format.call(self)
|
280
280
|
elsif format == :conventional
|
281
|
-
|
281
|
+
conventional?
|
282
282
|
elsif format == :relaxed
|
283
|
-
|
283
|
+
relaxed?
|
284
284
|
elsif format == :redacted
|
285
|
-
|
285
|
+
redacted?
|
286
286
|
elsif format == :standard
|
287
|
-
|
287
|
+
standard?
|
288
288
|
elsif format == :none
|
289
289
|
true
|
290
290
|
else
|
@@ -295,13 +295,13 @@ module EmailAddress
|
|
295
295
|
# Returns the format of the address
|
296
296
|
def format?
|
297
297
|
# if :custom
|
298
|
-
if
|
298
|
+
if conventional?
|
299
299
|
:conventional
|
300
|
-
elsif
|
300
|
+
elsif relaxed?
|
301
301
|
:relax
|
302
|
-
elsif
|
302
|
+
elsif redacted?
|
303
303
|
:redacted
|
304
|
-
elsif
|
304
|
+
elsif standard?
|
305
305
|
:standard
|
306
306
|
else
|
307
307
|
:invalid
|
@@ -309,38 +309,38 @@ module EmailAddress
|
|
309
309
|
end
|
310
310
|
|
311
311
|
def valid_size?
|
312
|
-
return set_error(:local_size_long) if
|
313
|
-
if @host
|
312
|
+
return set_error(:local_size_long) if local.size > STANDARD_MAX_SIZE
|
313
|
+
if @host&.hosted_service?
|
314
314
|
return false if @config[:local_private_size] && !valid_size_checks(@config[:local_private_size])
|
315
|
-
|
316
|
-
return false
|
315
|
+
elsif @config[:local_size] && !valid_size_checks(@config[:local_size])
|
316
|
+
return false
|
317
317
|
end
|
318
318
|
return false if @config[:mailbox_size] && !valid_size_checks(@config[:mailbox_size])
|
319
319
|
true
|
320
320
|
end
|
321
321
|
|
322
322
|
def valid_size_checks(range)
|
323
|
-
return set_error(:local_size_short) if
|
324
|
-
return set_error(:local_size_long)
|
323
|
+
return set_error(:local_size_short) if mailbox.size < range.first
|
324
|
+
return set_error(:local_size_long) if mailbox.size > range.last
|
325
325
|
true
|
326
326
|
end
|
327
327
|
|
328
|
-
def valid_encoding?(enc
|
329
|
-
return false if enc == :ascii &&
|
328
|
+
def valid_encoding?(enc = @config[:local_encoding] || :ascii)
|
329
|
+
return false if enc == :ascii && unicode?
|
330
330
|
true
|
331
331
|
end
|
332
332
|
|
333
333
|
# True if the part matches the conventional format
|
334
334
|
def conventional?
|
335
335
|
self.syntax = :invalid
|
336
|
-
if
|
337
|
-
return false unless
|
338
|
-
|
336
|
+
if tag
|
337
|
+
return false unless mailbox =~ CONVENTIONAL_MAILBOX_REGEX &&
|
338
|
+
tag =~ CONVENTIONAL_TAG_REGEX
|
339
339
|
else
|
340
|
-
return false unless
|
340
|
+
return false unless CONVENTIONAL_MAILBOX_REGEX.match?(local)
|
341
341
|
end
|
342
|
-
|
343
|
-
|
342
|
+
valid_size? or return false
|
343
|
+
valid_encoding? or return false
|
344
344
|
self.syntax = :conventional
|
345
345
|
true
|
346
346
|
end
|
@@ -348,12 +348,14 @@ module EmailAddress
|
|
348
348
|
# Relaxed conventional is not so strict about character order.
|
349
349
|
def relaxed?
|
350
350
|
self.syntax = :invalid
|
351
|
-
|
352
|
-
|
353
|
-
if
|
354
|
-
return false unless
|
355
|
-
|
356
|
-
|
351
|
+
valid_size? or return false
|
352
|
+
valid_encoding? or return false
|
353
|
+
if tag
|
354
|
+
return false unless RELAXED_MAILBOX_REGEX.match?(mailbox) &&
|
355
|
+
RELAXED_TAG_REGEX.match?(tag)
|
356
|
+
self.syntax = :relaxed
|
357
|
+
true
|
358
|
+
elsif RELAXED_MAILBOX_REGEX.match?(local)
|
357
359
|
self.syntax = :relaxed
|
358
360
|
true
|
359
361
|
else
|
@@ -364,9 +366,9 @@ module EmailAddress
|
|
364
366
|
# True if the part matches the RFC standard format
|
365
367
|
def standard?
|
366
368
|
self.syntax = :invalid
|
367
|
-
|
368
|
-
|
369
|
-
if
|
369
|
+
valid_size? or return false
|
370
|
+
valid_encoding? or return false
|
371
|
+
if STANDARD_LOCAL_REGEX.match?(local)
|
370
372
|
self.syntax = :standard
|
371
373
|
true
|
372
374
|
else
|
@@ -379,26 +381,23 @@ module EmailAddress
|
|
379
381
|
def matches?(*rules)
|
380
382
|
rules.flatten.each do |r|
|
381
383
|
if r =~ /(.+)@\z/
|
382
|
-
return r if File.fnmatch?($1,
|
384
|
+
return r if File.fnmatch?($1, local)
|
383
385
|
end
|
384
386
|
end
|
385
387
|
false
|
386
388
|
end
|
387
389
|
|
388
|
-
def set_error(err, reason=nil)
|
390
|
+
def set_error(err, reason = nil)
|
389
391
|
@error = err
|
390
|
-
@reason= reason
|
391
|
-
@error_message = Config.error_message(err)
|
392
|
+
@reason = reason
|
393
|
+
@error_message = Config.error_message(err, locale)
|
392
394
|
false
|
393
395
|
end
|
394
396
|
|
395
|
-
|
396
|
-
@error_message
|
397
|
-
end
|
397
|
+
attr_reader :error_message
|
398
398
|
|
399
399
|
def error
|
400
|
-
|
400
|
+
valid? ? nil : (@error || :local_invalid)
|
401
401
|
end
|
402
|
-
|
403
402
|
end
|
404
403
|
end
|