#!/usr/bin/ruby wsp = '[\x20\x09]' vchar = '[\x21-\x7e]' quoted_pair = "\\\\(?:#{vchar}|#{wsp})" qtext = '[\x21\x23-\x5b\x5d-\x7e]' qcontent = "(?:#{qtext}|#{quoted_pair})" quoted_string = "\"#{qcontent}*\"" atext = '[a-zA-Z0-9!#$%&\'*+\-\/\=?^_`{|}~]' dot_atom_text = "#{atext}+(?:[.]#{atext}+)*" dot_atom = dot_atom_text local_part = "(?:#{dot_atom}|#{quoted_string})" domain = dot_atom addr_spec = "#{local_part}[@]#{domain}" puts 'addr_spec: ' + addr_spec dot_atom_loose = "#{atext}+(?:[.]|#{atext})*" local_part_loose = "(?:#{dot_atom_loose}|#{quoted_string})" addr_spec_loose = "#{local_part_loose}[@]#{domain}" puts 'addr_spec_loose: ' + addr_spec_loose valid = [ 'foo@example.com', # normal # local-part # dot-atom 'foo.hoge@example.com', 'foo.bar.baz@example.com', # quoted-string '"foo"@example.com', '"!"@example.com', # \x21 '"#"@example.com', # \x23 '"["@example.com', # \x5b '"]"@example.com', # \x5d '"["@example.com', # \x7e # quoted-pair '"foo\\ "@example.com', # \x20 "\"foo\\\x09\"\@example.com", # \x09 # php @ '"\\!"@example.com', # \x21 '"\\["@example.com', # \x7e # domain 'foo.hoge@localhost', 'foo.hoge@sub.example.com', ] valid_loose = [ 'foo.@docomo.ne.jp', 'foo.foo.@docomo.ne.jp', 'foo..@docomo.ne.jp', 'foo..foo@docomo.ne.jp', 'foo..foo.@docomo.ne.jp', ] invalid = [ '', 'foo', 'foo@', '@foo', # local-part # dot-atom '.foo@example.com', '..foo@example.com', 'foo@@example.com', 'foo[@example.com', 'foo @example.com', # quoted-string "\"\x00\"\@example.com", # \x00 # php @ '" "@example.com', # \x20 '"""@example.com', # \x22 '"\\"@example.com', # \x5c "\"\x7f\"\@example.com", # \x7f # php @ # quoted-pair "\"\\\x1f\"\@example.com", # \x1f # php @ "\"\\\x7f\"\@example.com", # \x7f # php @ # \z check "foo@example.com\n", "foo@example.com\nfoo@example.com", # non-ascii "\x80\@example.com", "\"\x80\"\@example.com", "\"\\\x80\"\@example.com", # utf8 "\x100\@example.com", "\"\x100\"\@example.com", "\"\\\x100\"\@example.com", ] $count = 0 def ok(regexp, addr, desc, xor = 0) $count = $count + 1 if (/\A#{regexp}\z/ =~ addr) ^ xor then puts "ok #{$count} - #{desc}"; else puts "not ok #{$count} - #{desc}"; end end def not_ok(regexp, addr, desc) ok(regexp, addr, desc, 1) end # normal valid.each do |addr| ok(addr_spec, addr, 'normal-valid - ' + addr); end (invalid + valid_loose).each do |addr| not_ok(addr_spec, addr, 'normal-invalid - ' + addr); end # loose (valid + valid_loose).each do |addr| ok(addr_spec_loose, addr, 'loose-valid - ' + addr); end invalid.each do |addr| not_ok(addr_spec_loose, addr, 'loose-invalid - ' + addr); end