sisimai 4.25.17-java → 5.0.0-java
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 +5 -5
- data/.travis.yml +3 -3
- data/ANALYTICAL-PRECISION +2 -2
- data/Benchmarks.mk +3 -3
- data/CONTRIBUTING +1 -1
- data/ChangeLog.md +406 -407
- data/Developers.mk +5 -6
- data/Gemfile +1 -1
- data/Makefile +12 -12
- data/README-JA.md +142 -94
- data/README.md +282 -150
- data/Rakefile +9 -3
- data/Repository.mk +2 -3
- data/lib/sisimai/address.rb +118 -74
- data/lib/sisimai/arf.rb +84 -82
- data/lib/sisimai/datetime.rb +5 -52
- data/lib/sisimai/{data → fact}/json.rb +7 -9
- data/lib/sisimai/fact/yaml.rb +31 -0
- data/lib/sisimai/fact.rb +468 -0
- data/lib/sisimai/lhost/activehunter.rb +12 -14
- data/lib/sisimai/lhost/amavis.rb +11 -14
- data/lib/sisimai/lhost/amazonses.rb +37 -41
- data/lib/sisimai/lhost/amazonworkmail.rb +15 -18
- data/lib/sisimai/lhost/aol.rb +12 -14
- data/lib/sisimai/lhost/apachejames.rb +19 -21
- data/lib/sisimai/lhost/barracuda.rb +10 -12
- data/lib/sisimai/lhost/bigfoot.rb +21 -21
- data/lib/sisimai/lhost/biglobe.rb +15 -16
- data/lib/sisimai/lhost/courier.rb +20 -20
- data/lib/sisimai/lhost/domino.rb +23 -19
- data/lib/sisimai/lhost/einsundeins.rb +20 -16
- data/lib/sisimai/lhost/exchange2003.rb +30 -29
- data/lib/sisimai/lhost/exchange2007.rb +70 -58
- data/lib/sisimai/lhost/exim.rb +175 -161
- data/lib/sisimai/lhost/ezweb.rb +31 -56
- data/lib/sisimai/lhost/facebook.rb +21 -33
- data/lib/sisimai/lhost/fml.rb +43 -48
- data/lib/sisimai/lhost/gmail.rb +29 -29
- data/lib/sisimai/lhost/gmx.rb +18 -17
- data/lib/sisimai/lhost/googlegroups.rb +9 -10
- data/lib/sisimai/lhost/gsuite.rb +21 -27
- data/lib/sisimai/lhost/imailserver.rb +25 -39
- data/lib/sisimai/lhost/interscanmss.rb +28 -31
- data/lib/sisimai/lhost/kddi.rb +22 -28
- data/lib/sisimai/lhost/mailfoundry.rb +11 -12
- data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
- data/lib/sisimai/lhost/mailru.rb +33 -27
- data/lib/sisimai/lhost/mcafee.rb +21 -31
- data/lib/sisimai/lhost/messagelabs.rb +17 -20
- data/lib/sisimai/lhost/messagingserver.rb +40 -37
- data/lib/sisimai/lhost/mfilter.rb +15 -16
- data/lib/sisimai/lhost/mxlogic.rb +24 -23
- data/lib/sisimai/lhost/notes.rb +17 -17
- data/lib/sisimai/lhost/office365.rb +63 -27
- data/lib/sisimai/lhost/opensmtpd.rb +12 -13
- data/lib/sisimai/lhost/outlook.rb +12 -15
- data/lib/sisimai/lhost/postfix.rb +179 -129
- data/lib/sisimai/lhost/powermta.rb +12 -14
- data/lib/sisimai/lhost/qmail.rb +44 -47
- data/lib/sisimai/lhost/receivingses.rb +15 -20
- data/lib/sisimai/lhost/sendgrid.rb +34 -32
- data/lib/sisimai/lhost/sendmail.rb +66 -53
- data/lib/sisimai/lhost/surfcontrol.rb +19 -19
- data/lib/sisimai/lhost/v5sendmail.rb +45 -39
- data/lib/sisimai/lhost/verizon.rb +35 -39
- data/lib/sisimai/lhost/x1.rb +18 -17
- data/lib/sisimai/lhost/x2.rb +17 -14
- data/lib/sisimai/lhost/x3.rb +19 -19
- data/lib/sisimai/lhost/x4.rb +72 -57
- data/lib/sisimai/lhost/x5.rb +17 -19
- data/lib/sisimai/lhost/x6.rb +41 -17
- data/lib/sisimai/lhost/yahoo.rb +17 -16
- data/lib/sisimai/lhost/yandex.rb +16 -20
- data/lib/sisimai/lhost/zoho.rb +16 -15
- data/lib/sisimai/lhost.rb +8 -10
- data/lib/sisimai/mail/maildir.rb +1 -3
- data/lib/sisimai/mail/mbox.rb +3 -4
- data/lib/sisimai/mail/memory.rb +0 -1
- data/lib/sisimai/mail/stdin.rb +1 -3
- data/lib/sisimai/mail.rb +3 -7
- data/lib/sisimai/mda.rb +28 -42
- data/lib/sisimai/message.rb +435 -325
- data/lib/sisimai/order.rb +5 -5
- data/lib/sisimai/reason/authfailure.rb +64 -0
- data/lib/sisimai/reason/badreputation.rb +53 -0
- data/lib/sisimai/reason/blocked.rb +94 -160
- data/lib/sisimai/reason/contenterror.rb +8 -9
- data/lib/sisimai/reason/delivered.rb +4 -6
- data/lib/sisimai/reason/exceedlimit.rb +10 -12
- data/lib/sisimai/reason/expired.rb +6 -8
- data/lib/sisimai/reason/feedback.rb +2 -3
- data/lib/sisimai/reason/filtered.rb +17 -19
- data/lib/sisimai/reason/hasmoved.rb +9 -10
- data/lib/sisimai/reason/hostunknown.rb +15 -15
- data/lib/sisimai/reason/mailboxfull.rb +10 -12
- data/lib/sisimai/reason/mailererror.rb +18 -20
- data/lib/sisimai/reason/mesgtoobig.rb +9 -11
- data/lib/sisimai/reason/networkerror.rb +5 -8
- data/lib/sisimai/reason/norelaying.rb +8 -11
- data/lib/sisimai/reason/notaccept.rb +13 -14
- data/lib/sisimai/reason/notcompliantrfc.rb +43 -0
- data/lib/sisimai/reason/onhold.rb +6 -9
- data/lib/sisimai/reason/policyviolation.rb +14 -12
- data/lib/sisimai/reason/rejected.rb +26 -24
- data/lib/sisimai/reason/requireptr.rb +69 -0
- data/lib/sisimai/reason/securityerror.rb +33 -36
- data/lib/sisimai/reason/spamdetected.rb +114 -147
- data/lib/sisimai/reason/speeding.rb +49 -0
- data/lib/sisimai/reason/suspend.rb +11 -11
- data/lib/sisimai/reason/syntaxerror.rb +11 -10
- data/lib/sisimai/reason/systemerror.rb +7 -9
- data/lib/sisimai/reason/systemfull.rb +7 -8
- data/lib/sisimai/reason/toomanyconn.rb +9 -11
- data/lib/sisimai/reason/undefined.rb +2 -3
- data/lib/sisimai/reason/userunknown.rb +129 -146
- data/lib/sisimai/reason/vacation.rb +3 -4
- data/lib/sisimai/reason/virusdetected.rb +10 -11
- data/lib/sisimai/reason.rb +59 -64
- data/lib/sisimai/rfc1894.rb +55 -28
- data/lib/sisimai/rfc2045.rb +373 -0
- data/lib/sisimai/rfc3464.rb +250 -308
- data/lib/sisimai/rfc3834.rb +42 -45
- data/lib/sisimai/rfc5322.rb +75 -100
- data/lib/sisimai/rfc5965.rb +31 -0
- data/lib/sisimai/rhost/cox.rb +5 -6
- data/lib/sisimai/rhost/franceptt.rb +6 -8
- data/lib/sisimai/rhost/godaddy.rb +12 -12
- data/lib/sisimai/rhost/{googleapps.rb → google.rb} +80 -72
- data/lib/sisimai/rhost/iua.rb +9 -10
- data/lib/sisimai/rhost/kddi.rb +6 -8
- data/lib/sisimai/rhost/{exchangeonline.rb → microsoft.rb} +115 -114
- data/lib/sisimai/rhost/mimecast.rb +42 -40
- data/lib/sisimai/rhost/nttdocomo.rb +13 -18
- data/lib/sisimai/rhost/spectrum.rb +10 -12
- data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
- data/lib/sisimai/rhost.rb +23 -31
- data/lib/sisimai/smtp/command.rb +59 -0
- data/lib/sisimai/smtp/error.rb +4 -7
- data/lib/sisimai/smtp/reply.rb +161 -74
- data/lib/sisimai/smtp/status.rb +504 -393
- data/lib/sisimai/smtp/transcript.rb +124 -0
- data/lib/sisimai/smtp.rb +0 -1
- data/lib/sisimai/string.rb +74 -5
- data/lib/sisimai/time.rb +1 -2
- data/lib/sisimai/version.rb +1 -1
- data/lib/sisimai.rb +35 -21
- data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
- data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
- data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
- data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
- data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
- data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
- data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
- data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
- data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
- data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
- data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
- data/sisimai-java.gemspec +1 -1
- data/sisimai.gemspec +1 -1
- metadata +42 -23
- data/.rspec +0 -2
- data/lib/sisimai/data/yaml.rb +0 -33
- data/lib/sisimai/data.rb +0 -411
- data/lib/sisimai/mime.rb +0 -456
- data/set-of-emails/maildir/mac/reported-from-nick4tech-san-01.eml +0 -6
- /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
data/lib/sisimai/address.rb
CHANGED
@@ -1,9 +1,34 @@
|
|
1
1
|
module Sisimai
|
2
2
|
# Sisimai::Address provide methods for dealing email address.
|
3
3
|
class Address
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
build_regular_expressions = lambda do
|
5
|
+
# See http://www.ietf.org/rfc/rfc5322.txt
|
6
|
+
# or http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html ...
|
7
|
+
# addr-spec = local-part "@" domain
|
8
|
+
# local-part = dot-atom / quoted-string / obs-local-part
|
9
|
+
# domain = dot-atom / domain-literal / obs-domain
|
10
|
+
# domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
|
11
|
+
# dcontent = dtext / quoted-pair
|
12
|
+
# dtext = NO-WS-CTL / ; Non white space controls
|
13
|
+
# %d33-90 / ; The rest of the US-ASCII
|
14
|
+
# %d94-126 ; characters not including "[", "]", or "\"
|
15
|
+
re = { rfc5322: nil, ignored: nil, domain: nil }
|
16
|
+
atom = %r([a-zA-Z0-9_!#\$\%&'*+/=?\^`{}~|\-]+)o
|
17
|
+
quoted_string = %r/"(?:\\[^\r\n]|[^\\"])*"/o
|
18
|
+
domain_literal = %r/\[(?:\\[\x01-\x09\x0B-\x0c\x0e-\x7f]|[\x21-\x5a\x5e-\x7e])*\]/o
|
19
|
+
dot_atom = %r/#{atom}(?:[.]#{atom})*/o
|
20
|
+
local_part = %r/(?:#{dot_atom}|#{quoted_string})/o
|
21
|
+
domain = %r/(?:#{dot_atom}|#{domain_literal})/o
|
22
|
+
|
23
|
+
re[:rfc5322] = %r/\A#{local_part}[@]#{domain}\z/o
|
24
|
+
re[:ignored] = %r/\A#{local_part}[.]*[@]#{domain}\z/o
|
25
|
+
re[:domain] = %r/\A#{domain}\z/o
|
26
|
+
|
27
|
+
return re
|
28
|
+
end
|
29
|
+
|
30
|
+
Re = build_regular_expressions.call
|
31
|
+
Indicators = {
|
7
32
|
:'email-address' => (1 << 0), # <neko@example.org>
|
8
33
|
:'quoted-string' => (1 << 1), # "Neko, Nyaan"
|
9
34
|
:'comment-block' => (1 << 2), # (neko)
|
@@ -11,36 +36,44 @@ module Sisimai
|
|
11
36
|
Delimiters = { '<' => 1, '>' => 1, '(' => 1, ')' => 1, '"' => 1, ',' => 1 }.freeze
|
12
37
|
|
13
38
|
# Return pseudo recipient or sender address
|
14
|
-
# @param [Symbol]
|
15
|
-
# @return [String,
|
16
|
-
#
|
17
|
-
def self.undisclosed(
|
18
|
-
return
|
19
|
-
return nil unless %w[r s].index(argv1)
|
20
|
-
|
21
|
-
local = argv1 == 'r' ? 'recipient' : 'sender'
|
22
|
-
return sprintf('undisclosed-%s-in-headers@libsisimai.org.invalid', local)
|
39
|
+
# @param [Symbol] argv0 Address type: true = recipient, false = sender
|
40
|
+
# @return [String, nil] Pseudo recipient address or sender address or nil when the argv1 is
|
41
|
+
# neither :r nor :s
|
42
|
+
def self.undisclosed(argv0 = false)
|
43
|
+
return sprintf('undisclosed-%s-in-headers@libsisimai.org.invalid', argv0 ? 'recipient' : 'sender')
|
23
44
|
end
|
24
45
|
|
25
|
-
#
|
26
|
-
# @param [
|
27
|
-
# @return [
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
return
|
33
|
-
return
|
34
|
-
return
|
35
|
-
|
36
|
-
thing = Sisimai::Address.new(argvs[:address])
|
37
|
-
return nil unless thing
|
38
|
-
return nil if thing.void
|
39
|
-
|
40
|
-
thing.name = argvs[:name] || ''
|
41
|
-
thing.comment = argvs[:comment] || ''
|
46
|
+
# Check that the argument is an email address or not
|
47
|
+
# @param [String] email Email address string
|
48
|
+
# @return [True,False] true: is an email address
|
49
|
+
# false: is not an email address
|
50
|
+
def self.is_emailaddress(email)
|
51
|
+
return false unless email.is_a?(::String)
|
52
|
+
return false if email =~ %r/(?:[\x00-\x1f]|\x1f)/
|
53
|
+
return false if email.size > 254
|
54
|
+
return true if email =~ Re[:ignored]
|
55
|
+
return false
|
56
|
+
end
|
42
57
|
|
43
|
-
|
58
|
+
# Check that the argument is mailer-daemon or not
|
59
|
+
# @param [String] argv0 Email address
|
60
|
+
# @return [True,False] true: mailer-daemon
|
61
|
+
# false: Not mailer-daemon
|
62
|
+
def self.is_mailerdaemon(argv0 = nil)
|
63
|
+
return false unless argv0
|
64
|
+
return false unless argv0.size > 0
|
65
|
+
return false unless argv0.is_a?(::String)
|
66
|
+
|
67
|
+
email = argv0.downcase
|
68
|
+
postmaster = [
|
69
|
+
'mailer-daemon@', '<mailer-daemon>', '(mailer-daemon)', ' mailer-daemon ',
|
70
|
+
'postmaster@', '<postmaster>', '(postmaster)'
|
71
|
+
].freeze
|
72
|
+
|
73
|
+
return true if postmaster.any? { |a| email.include?(a) }
|
74
|
+
return true if email == 'mailer-daemon'
|
75
|
+
return true if email == 'postmaster'
|
76
|
+
return false
|
44
77
|
end
|
45
78
|
|
46
79
|
def self.find(argv1 = nil, addrs = false)
|
@@ -48,8 +81,7 @@ module Sisimai
|
|
48
81
|
# @param [String] argv1 String including email address
|
49
82
|
# @param [Boolean] addrs true: Returns list including all the elements
|
50
83
|
# false: Returns list including email addresses only
|
51
|
-
# @return [Array, Nil] Email address list or nil when there is no
|
52
|
-
# email address in the argument
|
84
|
+
# @return [Array, Nil] Email address list or nil when there is no email address in the argument
|
53
85
|
# @example Parse email address
|
54
86
|
# find('Neko <neko(nyaan)@example.org>')
|
55
87
|
# #=> [{ address: 'neko@example.org', name: 'Neko', comment: '(nyaan)'}]
|
@@ -98,7 +130,7 @@ module Sisimai
|
|
98
130
|
if e == '<'
|
99
131
|
# <: The beginning of an email address or not
|
100
132
|
if v[:address].size > 0
|
101
|
-
p.empty?
|
133
|
+
p.empty? ? (v[:name] << e) : (v[p] << e)
|
102
134
|
else
|
103
135
|
# <neko@nyaan.example.org>
|
104
136
|
readcursor |= Indicators[:'email-address']
|
@@ -213,18 +245,19 @@ module Sisimai
|
|
213
245
|
# String like an email address will be set to the value of "address"
|
214
246
|
v[:address] = cv[1] + '@' + cv[2]
|
215
247
|
|
216
|
-
elsif Sisimai::
|
248
|
+
elsif Sisimai::Address.is_mailerdaemon(v[:name])
|
217
249
|
# Allow if the argument is MAILER-DAEMON
|
218
250
|
v[:address] = v[:name]
|
219
251
|
end
|
220
252
|
|
221
253
|
unless v[:address].empty?
|
222
254
|
# Remove the comment from the address
|
223
|
-
if
|
224
|
-
# (nyaan)nekochan@example.org, nekochan(nyaan)cat@example.org or
|
225
|
-
|
226
|
-
v[:address]
|
227
|
-
v[:
|
255
|
+
if Sisimai::String.aligned(v[:address], ['(', ')'])
|
256
|
+
# (nyaan)nekochan@example.org, nekochan(nyaan)cat@example.org or nekochan(nyaan)@example.org
|
257
|
+
p1 = v[:address].index('(')
|
258
|
+
p2 = v[:address].index(')')
|
259
|
+
v[:address] = v[:address][0, p1] + v[:address][p2 + 1, v[:address].size]
|
260
|
+
v[:comment] = v[:address][p1, p2 - p1 - 1]
|
228
261
|
end
|
229
262
|
readbuffer << v
|
230
263
|
end
|
@@ -233,15 +266,15 @@ module Sisimai
|
|
233
266
|
while e = readbuffer.shift do
|
234
267
|
# The element must not include any character except from 0x20 to 0x7e.
|
235
268
|
next if e[:address] =~ /[^\x20-\x7e]/
|
236
|
-
|
269
|
+
if e[:address].include?('@') == false
|
237
270
|
# Allow if the argument is MAILER-DAEMON
|
238
|
-
next unless Sisimai::
|
271
|
+
next unless Sisimai::Address.is_mailerdaemon(e[:address])
|
239
272
|
end
|
240
273
|
|
241
|
-
# Remove angle brackets, other brackets, and quotations: []<>{}'`
|
242
|
-
#
|
243
|
-
e[:address] = e[:address].
|
244
|
-
e[:address]
|
274
|
+
# Remove angle brackets, other brackets, and quotations: []<>{}'` except a domain part is
|
275
|
+
# an IP address like neko@[192.0.2.222]
|
276
|
+
e[:address] = e[:address].gsub(/\A[\[<{('`]/, '').gsub(/[.,'`>});]\z/, '')
|
277
|
+
e[:address] = e[:address].gsub(/[^A-Za-z]\z/, '') unless e[:address].include?('@[')
|
245
278
|
e[:address] = e[:address].sub(/\A["]/, '').chomp('"') unless e[:address] =~ /\A["].+["][@]/
|
246
279
|
|
247
280
|
if addrs
|
@@ -271,7 +304,7 @@ module Sisimai
|
|
271
304
|
return nil unless input
|
272
305
|
return input unless input.is_a? Object::String
|
273
306
|
|
274
|
-
addrs = Sisimai::Address.find(input,
|
307
|
+
addrs = Sisimai::Address.find(input, true) || []
|
275
308
|
return input if addrs.empty?
|
276
309
|
return addrs[0][:address]
|
277
310
|
end
|
@@ -285,7 +318,7 @@ module Sisimai
|
|
285
318
|
return nil unless email.is_a? Object::String
|
286
319
|
return nil unless cv = email.split('@', 2).first.match(/\A[-\w]+?[+](\w[-.\w]+\w)[=](\w[-.\w]+\w)\z/)
|
287
320
|
verp0 = cv[1] + '@' + cv[2]
|
288
|
-
return verp0 if Sisimai::
|
321
|
+
return verp0 if Sisimai::Address.is_emailaddress(verp0)
|
289
322
|
end
|
290
323
|
|
291
324
|
# Expand alias: remove from '+' to '@'
|
@@ -294,7 +327,7 @@ module Sisimai
|
|
294
327
|
# @example Expand alias
|
295
328
|
# expand_alias('neko+straycat@example.org') #=> 'neko@example.org'
|
296
329
|
def self.expand_alias(email)
|
297
|
-
return nil unless Sisimai::
|
330
|
+
return nil unless Sisimai::Address.is_emailaddress(email)
|
298
331
|
|
299
332
|
local = email.split('@')
|
300
333
|
return nil unless cv = local[0].match(/\A([-\w]+?)[+].+\z/)
|
@@ -312,62 +345,72 @@ module Sisimai
|
|
312
345
|
attr_accessor :name, :comment
|
313
346
|
|
314
347
|
# Constructor of Sisimai::Address
|
315
|
-
# @param
|
316
|
-
# @return
|
317
|
-
#
|
318
|
-
def initialize(
|
319
|
-
return nil unless
|
320
|
-
|
321
|
-
|
322
|
-
return nil unless addrs
|
323
|
-
return nil if addrs.empty?
|
348
|
+
# @param [Hash] argvs Email address, name, and other elements
|
349
|
+
# @return [Sisimai::Address] Object or nil when the email address was not valid.
|
350
|
+
# @example new({address: 'neko@example.org', name: 'Neko', comment: '(nyaan)')} # => Sisimai::Address object
|
351
|
+
def initialize(argvs)
|
352
|
+
return nil unless argvs.is_a? Hash
|
353
|
+
return nil unless argvs[:address]
|
354
|
+
return nil if argvs[:address].empty?
|
324
355
|
|
325
|
-
thing = addrs.shift
|
326
356
|
heads = ['<']
|
327
357
|
tails = ['>', ',', '.', ';']
|
328
|
-
|
329
|
-
if cv =
|
330
|
-
|
358
|
+
point = argvs[:address].rindex('@')
|
359
|
+
if cv = argvs[:address].match(/\A([^\s]+)[@]([^@]+)\z/) ||
|
360
|
+
argvs[:address].match(/\A(["].+?["])[@]([^@]+)\z/)
|
331
361
|
# Get the local part and the domain part from the email address
|
332
|
-
lpart =
|
333
|
-
dpart =
|
334
|
-
email = Sisimai::Address.expand_verp(
|
362
|
+
lpart = argvs[:address][0, point]
|
363
|
+
dpart = argvs[:address][point + 1, argvs[:address].size]
|
364
|
+
email = Sisimai::Address.expand_verp(argvs[:address])
|
335
365
|
aname = nil
|
336
366
|
|
337
367
|
unless email
|
338
368
|
# Is not VERP address, try to expand the address as an alias
|
339
|
-
email = Sisimai::Address.expand_alias(
|
369
|
+
email = Sisimai::Address.expand_alias(argvs[:address]) || ''
|
340
370
|
aname = true unless email.empty?
|
341
371
|
end
|
342
372
|
|
343
|
-
if email
|
373
|
+
if email.include?('@')
|
344
374
|
# The address is a VERP or an alias
|
345
375
|
if aname
|
346
376
|
# The address is an alias: neko+nyaan@example.jp
|
347
|
-
@alias =
|
377
|
+
@alias = argvs[:address]
|
348
378
|
else
|
349
379
|
# The address is a VERP: b+neko=example.jp@example.org
|
350
|
-
@verp =
|
380
|
+
@verp = argvs[:address]
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
heads.each do |e|
|
385
|
+
while lpart[0, 1] == e
|
386
|
+
lpart[0, 1] = ''
|
351
387
|
end
|
352
388
|
end
|
389
|
+
|
390
|
+
tails.each do |e|
|
391
|
+
while dpart[-1, 1] == e
|
392
|
+
dpart[-1, 1] = ''
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
353
396
|
@user = lpart
|
354
397
|
@host = dpart
|
355
398
|
@address = lpart + '@' + dpart
|
356
399
|
else
|
357
400
|
# The argument does not include "@"
|
358
|
-
return nil unless Sisimai::
|
359
|
-
return nil if
|
401
|
+
return nil unless Sisimai::Address.is_mailerdaemon(argvs[:address])
|
402
|
+
return nil if argvs[:address].include?(' ')
|
360
403
|
|
361
404
|
# The argument does not include " "
|
362
|
-
@user =
|
405
|
+
@user = argvs[:address]
|
363
406
|
@host ||= ''
|
364
|
-
@address =
|
407
|
+
@address = argvs[:address]
|
365
408
|
end
|
366
409
|
|
367
410
|
@alias ||= ''
|
368
411
|
@verp ||= ''
|
369
|
-
@name =
|
370
|
-
@comment =
|
412
|
+
@name = argvs[:name] || ''
|
413
|
+
@comment = argvs[:comment] || ''
|
371
414
|
end
|
372
415
|
|
373
416
|
# Check whether the object has valid content or not
|
@@ -391,3 +434,4 @@ module Sisimai
|
|
391
434
|
|
392
435
|
end
|
393
436
|
end
|
437
|
+
|
data/lib/sisimai/arf.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Sisimai
|
2
2
|
# Sisimai::ARF is a parser for email returned as a FeedBack Loop report message.
|
3
3
|
module ARF
|
4
|
-
# Imported from p5-Sisimail/lib/Sisimai/ARF.pm
|
5
4
|
class << self
|
6
5
|
require 'sisimai/lhost'
|
7
6
|
require 'sisimai/rfc5322'
|
@@ -15,23 +14,18 @@ module Sisimai
|
|
15
14
|
# Abusix ARF uses this is an autogenerated email abuse complaint regarding your network.
|
16
15
|
Indicators = Sisimai::Lhost.INDICATORS
|
17
16
|
StartingOf = {
|
18
|
-
rfc822:
|
19
|
-
report:
|
17
|
+
rfc822: ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'],
|
18
|
+
report: ['Content-Type: message/feedback-report'],
|
19
|
+
message: [
|
20
|
+
['this is a', 'abuse report'],
|
21
|
+
['this is a', 'authentication', 'failure report'],
|
22
|
+
['this is a', ' report for'],
|
23
|
+
['this is an authentication', 'failure report'],
|
24
|
+
['this is an autogenerated email abuse complaint'],
|
25
|
+
['this is an email abuse report'],
|
26
|
+
],
|
20
27
|
}.freeze
|
21
|
-
|
22
|
-
message: %r{\A(?>
|
23
|
-
[Tt]his[ ]is[ ]a[ ][^ ]+[ ](?:email[ ])?[Aa]buse[ ][Rr]eport
|
24
|
-
|[Tt]his[ ]is[ ]an[ ]email[ ]abuse[ ]report
|
25
|
-
|[Tt]his[ ]is[ ](?:
|
26
|
-
a[ ][^ ]+[ ]authentication[ -]failure[ ]report
|
27
|
-
|an[ ]authentication[ -]failure[ ]report
|
28
|
-
|an[ ]autogenerated[ ]email[ ]abuse[ ]complaint
|
29
|
-
|an?[ ][^ ]+[ ]report[ ]for
|
30
|
-
)
|
31
|
-
)
|
32
|
-
}x,
|
33
|
-
}.freeze
|
34
|
-
ReportFrom = /(?:staff[@]hotmail[.]com|complaints[@]email-abuse[.]amazonses[.]com)\z/.freeze
|
28
|
+
ReportFrom = ['staff@hotmail.com', 'complaints@email-abuse.amazonses.com'];
|
35
29
|
LongFields = Sisimai::RFC5322.LONGFIELDS
|
36
30
|
RFC822Head = Sisimai::RFC5322.HEADERFIELDS
|
37
31
|
|
@@ -45,19 +39,19 @@ module Sisimai
|
|
45
39
|
return false unless heads
|
46
40
|
match = false
|
47
41
|
|
48
|
-
if heads['content-type']
|
42
|
+
if Sisimai::String.aligned(heads['content-type'], ['report-type=', 'feedback-report'])
|
49
43
|
# Content-Type: multipart/report; report-type=feedback-report; ...
|
50
44
|
match = true
|
51
45
|
|
52
|
-
elsif heads['content-type'].
|
46
|
+
elsif heads['content-type'].include?('multipart/mixed')
|
53
47
|
# Microsoft (Hotmail, MSN, Live, Outlook) uses its own report format.
|
54
48
|
# Amazon SES Complaints bounces
|
55
|
-
|
56
|
-
if
|
49
|
+
cv = Sisimai::Address.s3s4(heads['from'])
|
50
|
+
if heads['subject'].include?('complaint about message from ')
|
57
51
|
# From: staff@hotmail.com
|
58
52
|
# From: complaints@email-abuse.amazonses.com
|
59
53
|
# Subject: complaint about message from 192.0.2.1
|
60
|
-
match = true
|
54
|
+
match = true if ReportFrom.any? { |a| cv.include?(a) }
|
61
55
|
end
|
62
56
|
end
|
63
57
|
return match
|
@@ -68,7 +62,7 @@ module Sisimai
|
|
68
62
|
# @param [String] mbody Message body of a bounce email
|
69
63
|
# @return [Hash] Bounce data list and message/rfc822 part
|
70
64
|
# @return [Nil] it failed to parse or the arguments are missing
|
71
|
-
def
|
65
|
+
def inquire(mhead, mbody)
|
72
66
|
return nil unless self.is_arf(mhead)
|
73
67
|
|
74
68
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
@@ -117,8 +111,9 @@ module Sisimai
|
|
117
111
|
# message-id of 0000-000000000000000000000000000000000@mx
|
118
112
|
# received from IP address 192.0.2.1 on
|
119
113
|
# Thu, 29 Apr 2010 00:00:00 +0900 (JST)
|
120
|
-
|
121
|
-
|
114
|
+
p = e.downcase
|
115
|
+
if commondata['diagnosis'].empty?
|
116
|
+
commondata['diagnosis'] = e if StartingOf[:message].any? { |a| Sisimai::String.aligned(p, a) }
|
122
117
|
end
|
123
118
|
|
124
119
|
if readcursor == 0
|
@@ -136,23 +131,22 @@ module Sisimai
|
|
136
131
|
|
137
132
|
if readcursor & Indicators[:'message-rfc822'] > 0
|
138
133
|
# message/rfc822 OR text/rfc822-headers part
|
139
|
-
if
|
134
|
+
if e.start_with?('X-HmXmrOriginalRecipient:')
|
140
135
|
# Microsoft ARF: original recipient.
|
141
|
-
dscontents[-1]['recipient'] = Sisimai::Address.s3s4(
|
136
|
+
dscontents[-1]['recipient'] = Sisimai::Address.s3s4(e[e.index(':') + 1, e.size])
|
142
137
|
recipients += 1
|
143
138
|
|
144
|
-
# The "X-HmXmrOriginalRecipient" header appears only once so
|
145
|
-
#
|
146
|
-
# Microsoft's implementation.
|
139
|
+
# The "X-HmXmrOriginalRecipient" header appears only once so we take this opportunity
|
140
|
+
# to hard-code ARF headers missing in Microsoft's implementation.
|
147
141
|
arfheaders['feedbacktype'] = 'abuse'
|
148
142
|
arfheaders['agent'] = 'Microsoft Junk Mail Reporting Program'
|
149
143
|
|
150
|
-
elsif
|
144
|
+
elsif e.start_with?('From: ')
|
151
145
|
# Microsoft ARF: original sender.
|
152
|
-
commondata['from'] = Sisimai::Address.s3s4(
|
146
|
+
commondata['from'] = Sisimai::Address.s3s4(e[6, e.size]) if commondata['from'].empty?
|
153
147
|
previousfn = 'from'
|
154
148
|
|
155
|
-
elsif e.start_with?(' '
|
149
|
+
elsif e.start_with?(' ')
|
156
150
|
# Continued line from the previous line
|
157
151
|
if previousfn == 'from'
|
158
152
|
# Multiple lines at "From:" field
|
@@ -166,7 +160,7 @@ module Sisimai
|
|
166
160
|
|
167
161
|
else
|
168
162
|
# Get required headers only
|
169
|
-
(lhs, rhs) = e.split(/:[ ]
|
163
|
+
(lhs, rhs) = e.split(/:[ ]/, 2)
|
170
164
|
next unless lhs
|
171
165
|
lhs.downcase!
|
172
166
|
|
@@ -191,10 +185,8 @@ module Sisimai
|
|
191
185
|
# Source-IP: 192.0.2.1
|
192
186
|
v = dscontents[-1]
|
193
187
|
|
194
|
-
if
|
195
|
-
|
196
|
-
# Original-Rcpt-To header field is optional and may appear any
|
197
|
-
# number of times as appropriate:
|
188
|
+
if e.start_with?('Original-Rcpt-To: ', 'Redacted-Address: ')
|
189
|
+
# Original-Rcpt-To header field is optional and may appear any number of times as appropriate:
|
198
190
|
# Original-Rcpt-To: <user@example.com>
|
199
191
|
# Redacted-Address: localpart@
|
200
192
|
if v['recipient']
|
@@ -202,48 +194,45 @@ module Sisimai
|
|
202
194
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
203
195
|
v = dscontents[-1]
|
204
196
|
end
|
205
|
-
v['recipient'] = Sisimai::Address.s3s4(
|
197
|
+
v['recipient'] = Sisimai::Address.s3s4(e[e.index(' ') + 1, e.size])
|
206
198
|
recipients += 1
|
207
199
|
|
208
|
-
elsif
|
200
|
+
elsif e.start_with?('Feedback-Type: ')
|
209
201
|
# The header field MUST appear exactly once.
|
210
202
|
# Feedback-Type: abuse
|
211
|
-
arfheaders['feedbacktype'] =
|
203
|
+
arfheaders['feedbacktype'] = e[e.index(' ') + 1, e.size]
|
212
204
|
|
213
|
-
elsif
|
214
|
-
# "Authentication-Results" indicates the result of one or more
|
215
|
-
#
|
216
|
-
#
|
217
|
-
|
218
|
-
# spf=fail smtp.mail=somespammer@example.com
|
219
|
-
arfheaders['authres'] = cv[1]
|
205
|
+
elsif e.start_with?('Authentication-Results: ')
|
206
|
+
# "Authentication-Results" indicates the result of one or more authentication checks
|
207
|
+
# run by the report generator.
|
208
|
+
# Authentication-Results: mail.example.jp; spf=fail smtp.mail=spammer@example.com
|
209
|
+
arfheaders['authres'] = e[e.index(' ') + 1, e.size]
|
220
210
|
|
221
|
-
elsif
|
211
|
+
elsif e.start_with?('User-Agent: ')
|
222
212
|
# The header field MUST appear exactly once.
|
223
213
|
# User-Agent: SomeGenerator/1.0
|
224
|
-
arfheaders['agent'] =
|
214
|
+
arfheaders['agent'] = e[e.index(' ') + 1, e.size]
|
225
215
|
|
226
|
-
elsif
|
227
|
-
# Arrival-Date header is optional and MUST NOT appear more than
|
228
|
-
# once.
|
216
|
+
elsif e.start_with?('Received-Date: ', 'Arrival-Date: ')
|
217
|
+
# Arrival-Date header is optional and MUST NOT appear more than once.
|
229
218
|
# Received-Date: Thu, 29 Apr 2010 00:00:00 JST
|
230
219
|
# Arrival-Date: Thu, 29 Apr 2010 00:00:00 +0000
|
231
|
-
arfheaders['date'] =
|
220
|
+
arfheaders['date'] = e[e.index(' ') + 1, e.size]
|
232
221
|
|
233
|
-
elsif
|
222
|
+
elsif e.start_with?('Reporting-MTA: dns; ')
|
234
223
|
# The header is optional and MUST NOT appear more than once.
|
235
224
|
# Reporting-MTA: dns; mx.example.jp
|
236
|
-
commondata['rhost'] =
|
225
|
+
commondata['rhost'] = e[e.index(';') + 2, e.size]
|
237
226
|
|
238
|
-
elsif
|
227
|
+
elsif e.start_with?('Source-IP: ')
|
239
228
|
# The header is optional and MUST NOT appear more than once.
|
240
229
|
# Source-IP: 192.0.2.45
|
241
|
-
arfheaders['rhost'] =
|
230
|
+
arfheaders['rhost'] = e[e.index(' ') + 1, e.size]
|
242
231
|
|
243
|
-
elsif
|
232
|
+
elsif e.start_with?('Original-Mail-From: ')
|
244
233
|
# the header is optional and MUST NOT appear more than once.
|
245
234
|
# Original-Mail-From: <somespammer@example.net>
|
246
|
-
commondata['from'] = Sisimai::Address.s3s4(
|
235
|
+
commondata['from'] = Sisimai::Address.s3s4(e[e.index(' ') + 1, e.size]) if commondata['from'].empty?
|
247
236
|
|
248
237
|
end
|
249
238
|
end
|
@@ -254,41 +243,51 @@ module Sisimai
|
|
254
243
|
commondata['diagnosis'] << ' ' << arfheaders['authres']
|
255
244
|
end
|
256
245
|
|
257
|
-
|
246
|
+
if recipients == 0
|
258
247
|
# The original recipient address was not found
|
259
|
-
if
|
248
|
+
if Sisimai::String.aligned(rfc822part, ["\nTo: ", '@'])
|
260
249
|
# pick the address from To: header in message/rfc822 part.
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
250
|
+
p1 = rfc822part.index("\nTo: ") + 5
|
251
|
+
p2 = rfc822part.index("\n", p1 + 1)
|
252
|
+
cm = p2 > 0 ? p2 - p1 : 255
|
253
|
+
dscontents[-1]['recipient'] = Sisimai::Address.s3s4(rfc822part[p1, cm])
|
254
|
+
recipients = 1
|
255
|
+
end
|
256
|
+
|
257
|
+
while true
|
258
|
+
# Insert pseudo recipient address when there is no valid recipient address in the message
|
259
|
+
# for example,
|
260
|
+
# Date: Thu, 29 Apr 2015 23:34:45 +0000
|
261
|
+
# To: "undisclosed"
|
262
|
+
# Subject: Nyaan
|
263
|
+
# Message-ID: <ffffffffffffffffffffffff00000000@example.net>
|
264
|
+
dscontents[-1]['recipient'] ||= ''
|
265
|
+
break if dscontents[-1]['recipient'].include?('@')
|
266
|
+
dscontents[-1]['recipient'] = Sisimai::Address.undisclosed(true)
|
267
|
+
recipients = 1
|
268
|
+
break
|
266
269
|
end
|
267
|
-
recipients = 1
|
268
270
|
end
|
269
271
|
|
270
|
-
unless rfc822part
|
271
|
-
# There is no "From:" header in the original message
|
272
|
-
#
|
272
|
+
unless Sisimai::String.aligned(rfc822part, ['From: ', '@'])
|
273
|
+
# There is no "From:" header in the original message. Append the value of "Original-Mail-From"
|
274
|
+
# value as a sender address.
|
273
275
|
rfc822part << 'From: ' << commondata['from'] + "\n" unless commondata['from'].empty?
|
274
276
|
end
|
275
277
|
|
276
|
-
if
|
278
|
+
if mhead['subject'].include?('complaint about message from ')
|
277
279
|
# Microsoft ARF: remote host address.
|
278
|
-
arfheaders['rhost'] =
|
280
|
+
arfheaders['rhost'] = mhead['subject'][mhead['subject'].rindex(' ') + 1, mhead['subject'].size]
|
279
281
|
commondata['diagnosis'] =
|
280
282
|
'This is a Microsoft email abuse report for an email message received from IP' << arfheaders['rhost'] + ' on ' << mhead['date']
|
281
283
|
end
|
282
284
|
|
283
285
|
dscontents.each do |e|
|
284
|
-
|
285
|
-
|
286
|
-
e['recipient'] = Sisimai::Address.s3s4(rcptintext)
|
287
|
-
end
|
286
|
+
# AOL = http://forums.cpanel.net/f43/aol-brutal-work-71473.html
|
287
|
+
e['recipient'] = Sisimai::Address.s3s4(rcptintext) if e['recipient'][-1, 1] == '@'
|
288
288
|
arfheaders.each_key { |a| e[a] ||= arfheaders[a] || '' }
|
289
289
|
e.delete('authres')
|
290
290
|
|
291
|
-
e['softbounce'] = -1
|
292
291
|
e['diagnosis'] = commondata['diagnosis'] unless e['diagnosis']
|
293
292
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
294
293
|
e['date'] = mhead['date'] if e['date'].empty?
|
@@ -302,10 +301,12 @@ module Sisimai
|
|
302
301
|
# The value of "Reporting-MTA" header
|
303
302
|
e['rhost'] = commondata['rhost']
|
304
303
|
|
305
|
-
|
306
|
-
#
|
307
|
-
# from IP address 24.64.1.1
|
308
|
-
|
304
|
+
else
|
305
|
+
# Try to get an IP address from the error message
|
306
|
+
# This is an email abuse report for an email message received from IP address 24.64.1.1
|
307
|
+
# on Thu, 29 Apr 2010 00:00:00 +0000
|
308
|
+
ip = Sisimai::String.ipv4(e['diagnosis']) || []
|
309
|
+
e['rhost'] = ip[0] if ip.size > 0
|
309
310
|
end
|
310
311
|
end
|
311
312
|
return { 'ds' => dscontents, 'rfc822' => rfc822part }
|
@@ -313,3 +314,4 @@ module Sisimai
|
|
313
314
|
end
|
314
315
|
end
|
315
316
|
end
|
317
|
+
|