Class: EmailAddress::Local
- Inherits:
-
Object
- Object
- EmailAddress::Local
- Defined in:
- lib/email_address/local.rb
Overview
EmailAddress Local part consists of
-
comments
-
mailbox
-
tag
Parsing id provider-dependent, but RFC allows: Chars: A-Z a-z 0-9 . ! # $ % ‘ * + - / = ? ^G _ { | } ~ Quoted: space ( ) , : ; < > @ [ ] Quoted-Backslash-Escaped: \ “ Quote local part or dot-separated sub-parts x.”y“.z RFC-5321 warns ”a host that expects to receive mail SHOULD avoid defining mailboxes
where the Local-part requires (or uses) the Quoted-string form".
(comment)mailbox | mailbox(comment) . can not appear at beginning or end, or appear consecutively 8-bit/UTF-8: allowed but mail-system defined RFC 5321 also warns that “a host that expects to receive mail SHOULD avoid
defining mailboxes where the Local-part requires (or uses) the Quoted-string form".
Postmaster: must always be case-insensitive Case: sensitive, but usually treated as equivalent Local Parts: comment, mailbox tag Length: up to 64 characters Note: gmail does allow “..” against RFC because they are ignored. This will
be fixed by collapsing consecutive punctuation in conventional formats,
and consider them typos.
RFC5322 Rules (Oct 2008):
addr-spec = local-part “@” domain local-part = dot-atom / quoted-string / obs-local-part domain = dot-atom / domain-literal / obs-domain domain-literal = [CFWS] “[” *([FWS] dtext) [FWS] “]” [CFWS] dtext = %d33-90 / ; Printable US-ASCII
%d94-126 / ; characters not including
obs-dtext ; "[", "]", or "\"
atext = ALPHA / DIGIT / ; Printable US-ASCII
"!" / "#" / ; characters not including
"$" / "%" / ; specials. Used for atoms.
"&" / "'" /
"*" / "+" /
"-" / "/" /
"=" / "?" /
"^" / "_" /
"`" / "{" /
"|" / "}" /
"~"
atom = [CFWS] 1*atext [CFWS] dot-atom-text = 1*atext *(“.” 1*atext) dot-atom = [CFWS] dot-atom-text [CFWS] specials = “(” / “)” / ; Special characters that do
"<" / ">" / ; not appear in atext
"[" / "]" /
":" / ";" /
"@" / "\" /
"," / "." /
DQUOTE
qtext = %d33 / ; Printable US-ASCII
%d35-91 / ; characters not including
%d93-126 / ; "\" or the quote character
obs-qtext
qcontent = qtext / quoted-pair quoted-string = [CFWS]
DQUOTE *([FWS] qcontent) [FWS] DQUOTE
[CFWS]
Constant Summary collapse
- BUSINESS_MAILBOXES =
RFC-2142: MAILBOX NAMES FOR COMMON SERVICES, ROLES AND FUNCTIONS
%w(info marketing sales support)
- NETWORK_MAILBOXES =
%w(abuse noc security)
- SERVICE_MAILBOXES =
%w(postmaster hostmaster usenet news webmaster www uucp ftp)
- SYSTEM_MAILBOXES =
Not from RFC-2142
%w(help mailer-daemon root)
- ROLE_MAILBOXES =
Not from RFC-2142
%w(staff office orders billing careers jobs)
- SPECIAL_MAILBOXES =
BUSINESS_MAILBOXES + NETWORK_MAILBOXES + SERVICE_MAILBOXES + SYSTEM_MAILBOXES + ROLE_MAILBOXES
- STANDARD_MAX_SIZE =
64
- CONVENTIONAL_MAILBOX_REGEX =
Conventional : word(word)*
/\A [\p{L}\p{N}]+ (?: [\.\-\+\'_] [\p{L}\p{N}]+ )* \z/x
- CONVENTIONAL_MAILBOX_WITHIN =
/[\p{L}\p{N}]+ (?: [\.\-\+\'_] [\p{L}\p{N}]+ )*/x
- RELAXED_MAILBOX_WITHIN =
Relaxed: same characters, relaxed order
/[\p{L}\p{N}]+ (?: [\.\-\+\'_]+ [\p{L}\p{N}]+ )*/x
- RELAXED_MAILBOX_REGEX =
/\A [\p{L}\p{N}]+ (?: [\.\-\+\'_]+ [\p{L}\p{N}]+ )* \z/x
- STANDARD_LOCAL_WITHIN =
RFC5322 Token: token.“token”.token (dot-separated tokens)
Quoted Token can also have: SPACE \" \\ ( ) , : ; < > @ [ \ ] .
/ (?: [\p{L}\p{N}\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\(\)]+ | \" (?: \\[\" \\] | [\x20-\x21\x23-\x2F\x3A-\x40\x5B\x5D-\x60\x7B-\x7E\p{L}\p{N}] )+ \" ) (?: \. (?: [\p{L}\p{N}\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\(\)]+ | \" (?: \\[\" \\] | [\x20-\x21\x23-\x2F\x3A-\x40\x5B\x5D-\x60\x7B-\x7E\p{L}\p{N}] )+ \" ) )* /x
- STANDARD_LOCAL_REGEX =
/\A #{STANDARD_LOCAL_WITHIN} \z/x
- REDACTED_REGEX =
sha1
/\A \{ [0-9a-f]{40} \} \z/x
Instance Attribute Summary collapse
-
#comment ⇒ Object
Returns the value of attribute comment.
-
#config ⇒ Object
Returns the value of attribute config.
-
#local ⇒ Object
Returns the value of attribute local.
-
#mailbox ⇒ Object
Returns the value of attribute mailbox.
-
#original ⇒ Object
Returns the value of attribute original.
-
#syntax ⇒ Object
Returns the value of attribute syntax.
-
#tag ⇒ Object
Returns the value of attribute tag.
Class Method Summary collapse
-
.redacted?(local) ⇒ Boolean
Returns true if the value matches the Redacted format.
Instance Method Summary collapse
-
#ascii? ⇒ Boolean
True if the the value contains only Latin characters (7-bit ASCII).
-
#canonical ⇒ Object
Returns a canonical form of the address.
-
#canonical! ⇒ Object
Sets the part to be the canonical form.
-
#conventional ⇒ Object
Returns a conventional form of the address.
-
#conventional! ⇒ Object
Sets the part to be the conventional form.
-
#conventional? ⇒ Boolean
True if the part matches the conventional format.
- #error ⇒ Object
- #error_message ⇒ Object
-
#format(form = @config[:local_format]||:conventional) ⇒ Object
Builds the local string according to configurations.
-
#format? ⇒ Boolean
Returns the format of the address.
-
#initialize(local, config = {}, host = nil) ⇒ Local
constructor
A new instance of Local.
-
#matches?(*rules) ⇒ Boolean
Matches configured formated form against File glob strings given.
-
#munge ⇒ Object
Returns the munged form of the address, like “ma*****”.
- #parse(raw) ⇒ Object
-
#parse_comment(raw) ⇒ Object
“(comment)mailbox” or “mailbox(comment)”, only one comment RFC Doesn’t say what to do if 2 comments occur, so last wins.
- #parse_tag(raw) ⇒ Object
-
#redacted? ⇒ Boolean
Returns true if the value matches the Redacted format.
-
#relax ⇒ Object
Relaxed format: mailbox and tag, no comment, no extended character set.
-
#relax! ⇒ Object
Dropps unusual parts of Standard form to form a relaxed version.
-
#relaxed? ⇒ Boolean
Relaxed conventional is not so strict about character order.
-
#root_name ⇒ Object
Mailbox with trailing numbers removed.
- #set_error(err, reason = nil) ⇒ Object
-
#special? ⇒ Boolean
Is the address for a common system or business role account?.
-
#standard ⇒ Object
Returns a normalized version of the standard address parts.
-
#standard? ⇒ Boolean
True if the part matches the RFC standard format.
- #to_s ⇒ Object
-
#unicode? ⇒ Boolean
True if the the value contains non-Latin Unicde characters.
-
#valid?(format = @config[:local_format]||:conventional) ⇒ Boolean
True if the part is valid according to the configurations.
- #valid_encoding?(enc = @config[:local_encoding]||:ascii) ⇒ Boolean
- #valid_size? ⇒ Boolean
- #valid_size_checks(range) ⇒ Object
Constructor Details
#initialize(local, config = {}, host = nil) ⇒ Local
Returns a new instance of Local.
104 105 106 107 108 109 |
# File 'lib/email_address/local.rb', line 104 def initialize(local, config={}, host=nil) self.config = config.empty? ? EmailAddress::Config.all_settings : config self.local = local @host = host @error = @error_message = nil end |
Instance Attribute Details
#comment ⇒ Object
Returns the value of attribute comment.
71 72 73 |
# File 'lib/email_address/local.rb', line 71 def comment @comment end |
#config ⇒ Object
Returns the value of attribute config.
71 72 73 |
# File 'lib/email_address/local.rb', line 71 def config @config end |
#local ⇒ Object
Returns the value of attribute local.
70 71 72 |
# File 'lib/email_address/local.rb', line 70 def local @local end |
#mailbox ⇒ Object
Returns the value of attribute mailbox.
71 72 73 |
# File 'lib/email_address/local.rb', line 71 def mailbox @mailbox end |
#original ⇒ Object
Returns the value of attribute original.
71 72 73 |
# File 'lib/email_address/local.rb', line 71 def original @original end |
#syntax ⇒ Object
Returns the value of attribute syntax.
72 73 74 |
# File 'lib/email_address/local.rb', line 72 def syntax @syntax end |
#tag ⇒ Object
Returns the value of attribute tag.
71 72 73 |
# File 'lib/email_address/local.rb', line 71 def tag @tag end |
Class Method Details
.redacted?(local) ⇒ Boolean
Returns true if the value matches the Redacted format
174 175 176 |
# File 'lib/email_address/local.rb', line 174 def self.redacted?(local) local =~ REDACTED_REGEX ? true : false end |
Instance Method Details
#ascii? ⇒ Boolean
True if the the value contains only Latin characters (7-bit ASCII)
159 160 161 |
# File 'lib/email_address/local.rb', line 159 def ascii? ! self.unicode? end |
#canonical ⇒ Object
Returns a canonical form of the address
212 213 214 215 216 217 218 |
# File 'lib/email_address/local.rb', line 212 def canonical if @config[:mailbox_canonical] @config[:mailbox_canonical].call(self.mailbox) else self.mailbox.downcase end end |
#canonical! ⇒ Object
Sets the part to be the canonical form
246 247 248 |
# File 'lib/email_address/local.rb', line 246 def canonical! self.local = self.canonical end |
#conventional ⇒ Object
Returns a conventional form of the address
203 204 205 206 207 208 209 |
# File 'lib/email_address/local.rb', line 203 def conventional if self.tag [self.mailbox, self.tag].join(@config[:tag_separator]) else self.mailbox end end |
#conventional! ⇒ Object
Sets the part to be the conventional form
241 242 243 |
# File 'lib/email_address/local.rb', line 241 def conventional! self.local = self.conventional end |
#conventional? ⇒ Boolean
True if the part matches the conventional format
329 330 331 332 333 334 335 336 |
# File 'lib/email_address/local.rb', line 329 def conventional? self.syntax = :invalid self.local =~ CONVENTIONAL_MAILBOX_REGEX or return false self.valid_size? or return false self.valid_encoding? or return false self.syntax = :conventional true end |
#error ⇒ Object
386 387 388 |
# File 'lib/email_address/local.rb', line 386 def error self.valid? ? nil : @error end |
#error_message ⇒ Object
382 383 384 |
# File 'lib/email_address/local.rb', line 382 def @error_message end |
#format(form = @config[:local_format]||:conventional) ⇒ Object
Builds the local string according to configurations
188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/email_address/local.rb', line 188 def format(form=@config[:local_format]||:conventional) if @config[:local_format].is_a?(Proc) @config[:local_format].call(self) elsif form == :conventional self.conventional elsif form == :canonical self.canonical elsif form == :relaxed self.relax elsif form == :standard self.standard end end |
#format? ⇒ Boolean
Returns the format of the address
291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/email_address/local.rb', line 291 def format? # if :custom if self.conventional? :conventional elsif self.relaxed? :relax elsif self.redacted? :redacted elsif self.standard? :standard else :invalid end end |
#matches?(*rules) ⇒ Boolean
Matches configured formated form against File glob strings given. Rules must end in @ to distinguish themselves from other email part matches.
366 367 368 369 370 371 372 373 |
# File 'lib/email_address/local.rb', line 366 def matches?(*rules) rules.flatten.each do |r| if r =~ /(.+)@\z/ return r if File.fnmatch?($1, self.local) end end false end |
#munge ⇒ Object
Returns the munged form of the address, like “ma*****”
256 257 258 |
# File 'lib/email_address/local.rb', line 256 def munge self.to_s.sub(/\A(.{1,2}).*/) { |m| $1 + @config[:munge_string] } end |
#parse(raw) ⇒ Object
125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/email_address/local.rb', line 125 def parse(raw) if raw =~ /\A\"(.*)\"\z/ # Quoted raw = $1 raw.gsub!(/\\(.)/, '\1') # Unescape elsif @config[:local_fix] && @config[:local_format] != :standard raw.gsub!(' ','') raw.gsub!(',','.') #raw.gsub!(/([^\p{L}\p{N}]{2,10})/) {|s| s[0] } # Stutter punctuation typo end raw, comment = self.parse_comment(raw) mailbox, tag = self.parse_tag(raw) mailbox ||= "" [mailbox, tag, comment] end |
#parse_comment(raw) ⇒ Object
“(comment)mailbox” or “mailbox(comment)”, only one comment RFC Doesn’t say what to do if 2 comments occur, so last wins
142 143 144 145 146 147 148 149 150 151 |
# File 'lib/email_address/local.rb', line 142 def parse_comment(raw) c = nil if raw =~ /\A\((.+?)\)(.+)\z/ c, raw = [$2, $1] end if raw =~ /\A(.+)\((.+?)\)\z/ raw, c = [$1, $2] end [raw, c] end |
#parse_tag(raw) ⇒ Object
153 154 155 156 |
# File 'lib/email_address/local.rb', line 153 def parse_tag(raw) separator = @config[:tag_separator] ||= '+' raw.split(separator, 2) end |
#redacted? ⇒ Boolean
Returns true if the value matches the Redacted format
169 170 171 |
# File 'lib/email_address/local.rb', line 169 def redacted? self.local =~ REDACTED_REGEX ? true : false end |
#relax ⇒ Object
Relaxed format: mailbox and tag, no comment, no extended character set
221 222 223 224 225 226 |
# File 'lib/email_address/local.rb', line 221 def relax form = self.mailbox form += @config[:tag_separator] + self.tag if self.tag form.gsub!(/[ \"\(\),:<>@\[\]\\]/,'') form end |
#relax! ⇒ Object
Dropps unusual parts of Standard form to form a relaxed version.
251 252 253 |
# File 'lib/email_address/local.rb', line 251 def relax! self.local = self.relax end |
#relaxed? ⇒ Boolean
Relaxed conventional is not so strict about character order.
339 340 341 342 343 344 345 346 347 348 349 |
# File 'lib/email_address/local.rb', line 339 def relaxed? self.syntax = :invalid self.valid_size? or return false self.valid_encoding? or return false if self.local =~ RELAXED_MAILBOX_REGEX self.syntax = :relaxed true else false end end |
#root_name ⇒ Object
Mailbox with trailing numbers removed
261 262 263 |
# File 'lib/email_address/local.rb', line 261 def root_name self.mailbox =~ /\A(.+?)\d+\z/ ? $1 : self.mailbox end |
#set_error(err, reason = nil) ⇒ Object
375 376 377 378 379 380 |
# File 'lib/email_address/local.rb', line 375 def set_error(err, reason=nil) @error = err @reason= reason @error_message = EmailAddress::Config.(err) false end |
#special? ⇒ Boolean
Is the address for a common system or business role account?
179 180 181 |
# File 'lib/email_address/local.rb', line 179 def special? SPECIAL_MAILBOXES.include?(mailbox) end |
#standard ⇒ Object
Returns a normalized version of the standard address parts.
229 230 231 232 233 234 235 236 237 238 |
# File 'lib/email_address/local.rb', line 229 def standard form = self.mailbox form += @config[:tag_separator] + self.tag if self.tag form += "(" + self.comment + ")" if self.comment form.gsub!(/([\\\"])/, '\\\1') # Escape \ and " if form =~ /[ \"\(\),:<>@\[\\\]]/ # Space and "(),:;<>@[\] form = %Q("#{form}") end form end |
#standard? ⇒ Boolean
True if the part matches the RFC standard format
352 353 354 355 356 357 358 359 360 361 362 |
# File 'lib/email_address/local.rb', line 352 def standard? self.syntax = :invalid self.valid_size? or return false self.valid_encoding? or return false if self.local =~ STANDARD_LOCAL_REGEX self.syntax = :standard true else false end end |
#to_s ⇒ Object
183 184 185 |
# File 'lib/email_address/local.rb', line 183 def to_s self.format end |
#unicode? ⇒ Boolean
True if the the value contains non-Latin Unicde characters
164 165 166 |
# File 'lib/email_address/local.rb', line 164 def unicode? self.local =~ /[^\p{InBasicLatin}]/ ? true : false end |
#valid?(format = @config[:local_format]||:conventional) ⇒ Boolean
True if the part is valid according to the configurations
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/email_address/local.rb', line 270 def valid?(format=@config[:local_format]||:conventional) if @config[:mailbox_validator].is_a?(Proc) @config[:mailbox_validator].call(self.mailbox, self.tag) elsif format.is_a?(Proc) format.call(self) elsif format == :conventional self.conventional? elsif format == :relaxed self.relaxed? elsif format == :redacted self.redacted? elsif format == :standard self.standard? elsif format == :none true else raise "Unknown format #{format}" end end |
#valid_encoding?(enc = @config[:local_encoding]||:ascii) ⇒ Boolean
323 324 325 326 |
# File 'lib/email_address/local.rb', line 323 def valid_encoding?(enc=@config[:local_encoding]||:ascii) return false if enc == :ascii && self.unicode? true end |
#valid_size? ⇒ Boolean
306 307 308 309 310 311 312 313 314 315 |
# File 'lib/email_address/local.rb', line 306 def valid_size? return set_error(:local_size_long) if self.local.size > STANDARD_MAX_SIZE if @host && @host.hosted_service? return false if @config[:local_private_size] && !valid_size_checks(@config[:local_private_size]) else return false if @config[:local_size] && !valid_size_checks(@config[:local_size]) end return false if @config[:mailbox_size] && !valid_size_checks(@config[:mailbox_size]) true end |
#valid_size_checks(range) ⇒ Object
317 318 319 320 321 |
# File 'lib/email_address/local.rb', line 317 def valid_size_checks(range) return set_error(:local_size_short) if self.mailbox.size < range.first return set_error(:local_size_long) if self.mailbox.size > range.last true end |