sisimai 4.25.16 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/ANALYTICAL-PRECISION +2 -2
- data/Benchmarks.mk +3 -3
- data/CONTRIBUTING +1 -1
- data/ChangeLog.md +412 -393
- data/Developers.mk +5 -6
- data/Gemfile +1 -1
- data/Makefile +15 -15
- data/README-JA.md +140 -78
- data/README.md +290 -143
- 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 +23 -18
- 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 -326
- 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 +12 -12
- 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 +41 -20
- 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/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/rfc3834.rb
CHANGED
@@ -1,67 +1,65 @@
|
|
1
1
|
module Sisimai
|
2
2
|
# Sisimai::RFC3834 - RFC3834 auto reply message detector
|
3
3
|
module RFC3834
|
4
|
-
# Imported from p5-Sisimail/lib/Sisimai/RFC3834.pm
|
5
4
|
class << self
|
6
5
|
# http://tools.ietf.org/html/rfc3834
|
7
|
-
MarkingsOf = { :boundary =>
|
8
|
-
|
9
|
-
|
10
|
-
'
|
11
|
-
'
|
12
|
-
'
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|automatic[ ]reply:
|
17
|
-
|out[ ]of[ ](?:the[ ])*office:
|
18
|
-
)
|
19
|
-
/x,
|
6
|
+
MarkingsOf = { :boundary => '__SISIMAI_PSEUDO_BOUNDARY__' }
|
7
|
+
LowerLabel = %w[from to subject auto-submitted precedence x-apple-action].freeze
|
8
|
+
DoNotParse = {
|
9
|
+
'from' => ['root@', 'postmaster@', 'mailer-daemon@'],
|
10
|
+
'to' => ['root@'],
|
11
|
+
'subject' => [
|
12
|
+
'security information for', # sudo(1)
|
13
|
+
'mail failure -', # Exim
|
14
|
+
],
|
20
15
|
}.freeze
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
'from' => %r/(?:root|postmaster|mailer-daemon)[@]/,
|
28
|
-
'to' => %r/root[@]/,
|
16
|
+
AutoReply0 = {
|
17
|
+
# http://www.iana.org/assignments/auto-submitted-keywords/auto-submitted-keywords.xhtml
|
18
|
+
'auto-submitted' => ['auto-generated', 'auto-replied', 'auto-notified'],
|
19
|
+
'precedence' => ['auto_reply'],
|
20
|
+
'subject' => ['auto:', 'auto response:', 'automatic reply:', 'out of office:', 'out of the office:'],
|
21
|
+
'x-apple-action' => ['vacation'],
|
29
22
|
}.freeze
|
30
23
|
SubjectSet = %r{\A(?>
|
31
|
-
(?:.+?)?
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
(?:.+?)?re:
|
25
|
+
|auto(?:[ ]response):
|
26
|
+
|automatic[ ]reply:
|
27
|
+
|out[ ]of[ ]office:
|
35
28
|
)
|
36
29
|
[ ]*(.+)\z
|
37
|
-
}
|
30
|
+
}x.freeze
|
38
31
|
|
39
32
|
# Detect auto reply message as RFC3834
|
40
33
|
# @param [Hash] mhead Message headers of a bounce email
|
41
34
|
# @param [String] mbody Message body of a bounce email
|
42
35
|
# @return [Hash] Bounce data list and message/rfc822 part
|
43
36
|
# @return [Nil] it failed to parse or the arguments are missing
|
44
|
-
def
|
37
|
+
def inquire(mhead, mbody)
|
45
38
|
leave = 0
|
46
39
|
match = 0
|
40
|
+
lower = {}
|
41
|
+
|
42
|
+
LowerLabel.each do |e|
|
43
|
+
# Set lower-cased value of each header related to auto-response
|
44
|
+
next unless mhead.has_key?(e)
|
45
|
+
lower[e] = mhead[e].downcase
|
46
|
+
end
|
47
47
|
|
48
48
|
# DETECT_EXCLUSION_MESSAGE
|
49
|
-
|
49
|
+
DoNotParse.each_key do |e|
|
50
50
|
# Exclude message from root@
|
51
|
-
next unless
|
52
|
-
next unless
|
53
|
-
next unless mhead[e].downcase =~ Excludings[e]
|
51
|
+
next unless lower[e]
|
52
|
+
next unless DoNotParse[e].any? { |a| lower[e].include?(a) }
|
54
53
|
leave = 1
|
55
54
|
break
|
56
55
|
end
|
57
56
|
return nil if leave > 0
|
58
57
|
|
59
|
-
#
|
60
|
-
|
58
|
+
# DETECT_AUTO_REPLY_MESSAGE0
|
59
|
+
AutoReply0.each_key do |e|
|
61
60
|
# RFC3834 Auto-Submitted and other headers
|
62
|
-
next unless
|
63
|
-
next unless
|
64
|
-
next unless mhead[e].downcase =~ AutoReply1[e]
|
61
|
+
next unless lower[e]
|
62
|
+
next unless AutoReply0[e].any? { |a| lower[e].include?(a) }
|
65
63
|
match += 1
|
66
64
|
break
|
67
65
|
end
|
@@ -95,16 +93,15 @@ module Sisimai
|
|
95
93
|
return nil unless recipients > 0
|
96
94
|
|
97
95
|
if mhead['content-type']
|
98
|
-
# Get the boundary string and set regular expression for matching with
|
99
|
-
|
100
|
-
|
101
|
-
MarkingsOf[:boundary] = %r/\A\Q#{b0}\E\z/ unless b0.empty?
|
96
|
+
# Get the boundary string and set regular expression for matching with the boundary string.
|
97
|
+
q = Sisimai::RFC2045.boundary(mhead['content-type'], 0) || ''
|
98
|
+
MarkingsOf[:boundary] = q unless q.empty?
|
102
99
|
end
|
103
100
|
|
104
101
|
# BODY_PARSER: Get vacation message
|
105
102
|
while e = bodyslices.shift do
|
106
103
|
# Read the first 5 lines except a blank line
|
107
|
-
countuntil += 1 if e
|
104
|
+
countuntil += 1 if e.include?(MarkingsOf[:boundary])
|
108
105
|
|
109
106
|
if e.empty?
|
110
107
|
# Check a blank line
|
@@ -113,8 +110,8 @@ module Sisimai
|
|
113
110
|
next
|
114
111
|
end
|
115
112
|
next unless e.include?(' ')
|
116
|
-
next if
|
117
|
-
next if
|
113
|
+
next if e.start_with?('Content-Type')
|
114
|
+
next if e.start_with?('Content-Transfer')
|
118
115
|
|
119
116
|
v['diagnosis'] ||= ''
|
120
117
|
v['diagnosis'] << e + ' '
|
@@ -127,7 +124,7 @@ module Sisimai
|
|
127
124
|
v['date'] = mhead['date']
|
128
125
|
v['status'] = ''
|
129
126
|
|
130
|
-
if cv =
|
127
|
+
if cv = lower['subject'].match(SubjectSet)
|
131
128
|
# Get the Subject header from the original message
|
132
129
|
rfc822part = 'Subject: ' << cv[1] + "\n"
|
133
130
|
end
|
data/lib/sisimai/rfc5322.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Sisimai
|
2
2
|
# Sisimai::RFC5322 provide methods for checking email address.
|
3
3
|
module RFC5322
|
4
|
-
# Imported from p5-Sisimail/lib/Sisimai/RFC5322.pm
|
5
4
|
class << self
|
5
|
+
require 'sisimai/string'
|
6
6
|
HeaderTable = {
|
7
7
|
:messageid => %w[message-id],
|
8
8
|
:subject => %w[subject],
|
@@ -12,46 +12,26 @@ module Sisimai
|
|
12
12
|
:recipient => %w[to delivered-to forward-path envelope-to x-envelope-to resent-to apparently-to],
|
13
13
|
}.freeze
|
14
14
|
|
15
|
-
build_regular_expressions = lambda do
|
16
|
-
# See http://www.ietf.org/rfc/rfc5322.txt
|
17
|
-
# or http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html ...
|
18
|
-
# addr-spec = local-part "@" domain
|
19
|
-
# local-part = dot-atom / quoted-string / obs-local-part
|
20
|
-
# domain = dot-atom / domain-literal / obs-domain
|
21
|
-
# domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
|
22
|
-
# dcontent = dtext / quoted-pair
|
23
|
-
# dtext = NO-WS-CTL / ; Non white space controls
|
24
|
-
# %d33-90 / ; The rest of the US-ASCII
|
25
|
-
# %d94-126 ; characters not including "[",
|
26
|
-
# ; "]", or "\"
|
27
|
-
re = { rfc5322: nil, ignored: nil, domain: nil }
|
28
|
-
atom = %r([a-zA-Z0-9_!#\$\%&'*+/=?\^`{}~|\-]+)o
|
29
|
-
quoted_string = %r/"(?:\\[^\r\n]|[^\\"])*"/o
|
30
|
-
domain_literal = %r/\[(?:\\[\x01-\x09\x0B-\x0c\x0e-\x7f]|[\x21-\x5a\x5e-\x7e])*\]/o
|
31
|
-
dot_atom = %r/#{atom}(?:[.]#{atom})*/o
|
32
|
-
local_part = %r/(?:#{dot_atom}|#{quoted_string})/o
|
33
|
-
domain = %r/(?:#{dot_atom}|#{domain_literal})/o
|
34
|
-
|
35
|
-
re[:rfc5322] = %r/\A#{local_part}[@]#{domain}\z/o
|
36
|
-
re[:ignored] = %r/\A#{local_part}[.]*[@]#{domain}\z/o
|
37
|
-
re[:domain] = %r/\A#{domain}\z/o
|
38
|
-
|
39
|
-
return re
|
40
|
-
end
|
41
|
-
|
42
15
|
build_flatten_rfc822header_list = lambda do
|
43
|
-
# Convert HEADER: structured hash table to flatten hash table for being
|
44
|
-
# called from Sisimai::Lhost::*
|
16
|
+
# Convert HEADER: structured hash table to flatten hash table for being called from Sisimai::Lhost::*
|
45
17
|
fv = {}
|
46
18
|
HeaderTable.each_value do |e|
|
47
19
|
e.each { |ee| fv[ee] = true }
|
48
20
|
end
|
49
21
|
return fv
|
50
22
|
end
|
51
|
-
|
52
|
-
Re = build_regular_expressions.call
|
53
23
|
HeaderIndex = build_flatten_rfc822header_list.call
|
54
24
|
|
25
|
+
def FIELDINDEX
|
26
|
+
return %w[
|
27
|
+
Resent-Date From Sender Reply-To To Message-ID Subject Return-Path Received Date X-Mailer
|
28
|
+
Content-Type Content-Transfer-Encoding Content-Description Content-Disposition
|
29
|
+
]
|
30
|
+
# The following fields are not referred in Sisimai
|
31
|
+
# Resent-From Resent-Sender Resent-Cc Cc Bcc Resent-Bcc In-Reply-To References
|
32
|
+
# Comments Keywords
|
33
|
+
end
|
34
|
+
|
55
35
|
# Grouped RFC822 headers
|
56
36
|
# @param [Symbol] group RFC822 Header group name
|
57
37
|
# @return [Array,Hash] RFC822 Header list
|
@@ -67,66 +47,37 @@ module Sisimai
|
|
67
47
|
return { 'to' => true, 'from' => true, 'subject' => true, 'message-id' => true }
|
68
48
|
end
|
69
49
|
|
70
|
-
# Check that the argument is an email address or not
|
71
|
-
# @param [String] email Email address string
|
72
|
-
# @return [True,False] true: is an email address
|
73
|
-
# false: is not an email address
|
74
|
-
def is_emailaddress(email)
|
75
|
-
return false unless email.is_a?(::String)
|
76
|
-
return false if email =~ %r/(?:[\x00-\x1f]|\x1f)/
|
77
|
-
return false if email.size > 254
|
78
|
-
return true if email =~ Re[:ignored]
|
79
|
-
return false
|
80
|
-
end
|
81
|
-
|
82
|
-
# Check that the argument is mailer-daemon or not
|
83
|
-
# @param [String] email Email address
|
84
|
-
# @return [True,False] true: mailer-daemon
|
85
|
-
# false: Not mailer-daemon
|
86
|
-
def is_mailerdaemon(email)
|
87
|
-
return false unless email.is_a?(::String)
|
88
|
-
regex = %r/(?:
|
89
|
-
(?:mailer-daemon|postmaster)[@]
|
90
|
-
|[<(](?:mailer-daemon|postmaster)[)>]
|
91
|
-
|\A(?:mailer-daemon|postmaster)\z
|
92
|
-
|[ ]?mailer-daemon[ ]
|
93
|
-
)
|
94
|
-
/x.freeze
|
95
|
-
return true if email.downcase =~ regex
|
96
|
-
return false
|
97
|
-
end
|
98
|
-
|
99
50
|
# Convert Received headers to a structured data
|
100
|
-
# @param [String]
|
51
|
+
# @param [String] argv1 Received header
|
101
52
|
# @return [Array] Received header as a structured data
|
102
|
-
def received(
|
103
|
-
return [] unless
|
53
|
+
def received(argv1)
|
54
|
+
return [] unless argv1.is_a?(::String)
|
104
55
|
|
105
56
|
hosts = []
|
106
57
|
value = { 'from' => '', 'by' => '' }
|
107
58
|
|
108
59
|
# Received: (qmail 10000 invoked by uid 999); 24 Apr 2013 00:00:00 +0900
|
109
|
-
return [] if
|
60
|
+
return [] if argv1.include?('(qmail ') && argv1.include?(' invoked ')
|
61
|
+
|
62
|
+
p1 = argv1.index('from ') || -1
|
63
|
+
p2 = argv1.index('by ') || -1
|
64
|
+
p3 = argv1.index(' ', p2 + 3) || -1
|
110
65
|
|
111
|
-
if
|
112
|
-
# Received: from localhost (localhost)
|
113
|
-
# by nijo.example.jp (V8/cf) id s1QB5ma0018057;
|
66
|
+
if p1 == 0 && p2 > 1 && p2 < p3
|
67
|
+
# Received: from localhost (localhost) by nijo.example.jp (V8/cf) id s1QB5ma0018057;
|
114
68
|
# Wed, 26 Feb 2014 06:05:48 -0500
|
115
|
-
value['from'] =
|
116
|
-
value['by'] =
|
117
|
-
|
118
|
-
elsif
|
119
|
-
# Received: by 10.70.22.98 with SMTP id c2mr1838265pdf.3; Fri, 18 Jul 2014
|
120
|
-
|
121
|
-
value['
|
122
|
-
value['by'] = cr[1]
|
69
|
+
value['from'] = Sisimai::String.sweep(argv1[p1 + 5, p2 - p1 - 5])
|
70
|
+
value['by'] = Sisimai::String.sweep(argv1[p2 + 3, p3 - p2 - 3])
|
71
|
+
|
72
|
+
elsif p1 != 0 && p2 > -1
|
73
|
+
# Received: by 10.70.22.98 with SMTP id c2mr1838265pdf.3; Fri, 18 Jul 2014 00:31:02 -0700 (PDT)
|
74
|
+
value['from'] = Sisimai::String.sweep(argv1[p2 + 3, argv1.size])
|
75
|
+
value['by'] = Sisimai::String.sweep(argv1[p2 + 3, p3 - p2 - 3])
|
123
76
|
end
|
124
77
|
|
125
78
|
if value['from'].include?(' ')
|
126
|
-
# Received: from [10.22.22.222] (smtp
|
127
|
-
# (
|
128
|
-
# by nijo.example.jp (V8/cf) with ESMTP id s1QB5ka0018055;
|
129
|
-
# Wed, 26 Feb 2014 06:05:47 -0500
|
79
|
+
# Received: from [10.22.22.222] (smtp.kyoto.ocn.ne.jp [192.0.2.222]) (authenticated bits=0)
|
80
|
+
# by nijo.example.jp (V8/cf) with ESMTP id s1QB5ka0018055; Wed, 26 Feb 2014 06:05:47 -0500
|
130
81
|
received = value['from'].split(' ')
|
131
82
|
namelist = []
|
132
83
|
addrlist = []
|
@@ -135,13 +86,13 @@ module Sisimai
|
|
135
86
|
|
136
87
|
while e = received.shift do
|
137
88
|
# Received: from [10.22.22.222] (smtp-gateway.kyoto.ocn.ne.jp [192.0.2.222])
|
138
|
-
|
89
|
+
cv = Sisimai::String.ipv4(e) || []
|
90
|
+
if cv.size > 0
|
139
91
|
# [192.0.2.1] or (192.0.2.1)
|
140
|
-
|
141
|
-
addrlist << e
|
92
|
+
addrlist.append(*cv)
|
142
93
|
else
|
143
94
|
# hostname
|
144
|
-
e = e.delete('()')
|
95
|
+
e = e.delete('()').strip
|
145
96
|
namelist << e
|
146
97
|
end
|
147
98
|
end
|
@@ -176,31 +127,55 @@ module Sisimai
|
|
176
127
|
return hosts
|
177
128
|
end
|
178
129
|
|
179
|
-
# Split given entire message body into error message lines and the original
|
180
|
-
#
|
181
|
-
# @param [String]
|
182
|
-
# @param [
|
183
|
-
#
|
130
|
+
# Split given entire message body into error message lines and the original message part only
|
131
|
+
# include email headers
|
132
|
+
# @param [String] email Entire message body
|
133
|
+
# @param [Array] cutby List of strings which is a boundary of the original message part
|
134
|
+
# @param [Bool] keeps Flag for keeping strings after "\n\n"
|
184
135
|
# @return [Array] [Error message lines, The original message]
|
185
|
-
# @since
|
186
|
-
def
|
187
|
-
return nil if
|
188
|
-
return nil
|
136
|
+
# @since v5.0.0
|
137
|
+
def part(email = '', cutby = [], keeps = false)
|
138
|
+
return nil if email.empty?
|
139
|
+
return nil if cutby.empty?
|
140
|
+
|
141
|
+
boundaryor = '' # A boundary string divides the error message part and the original message part
|
142
|
+
positionor = nil # A Position of the boundary string
|
143
|
+
formerpart = '' # The error message part
|
144
|
+
latterpart = '' # The original message part
|
145
|
+
|
146
|
+
cutby.each do |e|
|
147
|
+
# Find a boundary string(2nd argument) from the 1st argument
|
148
|
+
positionor = email.index(e); next unless positionor
|
149
|
+
boundaryor = e
|
150
|
+
break
|
151
|
+
end
|
189
152
|
|
190
|
-
|
191
|
-
|
153
|
+
if positionor
|
154
|
+
# There is the boundary string in the message body
|
155
|
+
formerpart = email[0, positionor]
|
156
|
+
latterpart = email[positionor + boundaryor.size + 1, email.size - positionor]
|
157
|
+
else
|
158
|
+
# Substitute the entire message to the former part when the boundary string is not included
|
159
|
+
# the "email"
|
160
|
+
formerpart = email
|
161
|
+
latterpart = ''
|
162
|
+
end
|
192
163
|
|
193
|
-
|
164
|
+
if latterpart.size > 0
|
194
165
|
# Remove blank lines, the message body of the original message, and append "\n" at the end
|
195
166
|
# of the original message headers
|
196
167
|
# 1. Remove leading blank lines
|
197
168
|
# 2. Remove text after the first blank line: \n\n
|
198
169
|
# 3. Append "\n" at the end of test block when the last character is not "\n"
|
199
|
-
|
200
|
-
|
201
|
-
|
170
|
+
latterpart.sub!(/\A[\r\n\s]+/, '')
|
171
|
+
if keeps == false
|
172
|
+
# Remove text after the first blank line: \n\n when "keeps" is false
|
173
|
+
latterpart = latterpart[0, latterpart.index("\n\n")] if latterpart.include?("\n\n")
|
174
|
+
end
|
175
|
+
latterpart << "\n" unless latterpart.end_with?("\n")
|
202
176
|
end
|
203
|
-
|
177
|
+
|
178
|
+
return [formerpart, latterpart]
|
204
179
|
end
|
205
180
|
|
206
181
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Sisimai
|
2
|
+
# Sisimai::RFC5965 - A class for An Extensible Format for Email Feedback Reports
|
3
|
+
module RFC5965
|
4
|
+
class << self
|
5
|
+
# https://datatracker.ietf.org/doc/html/rfc5965
|
6
|
+
def FIELDINDEX
|
7
|
+
return [
|
8
|
+
# Required Fields
|
9
|
+
# The following report header fields MUST appear exactly once:
|
10
|
+
'Feedback-Type', 'User-Agent', 'Version',
|
11
|
+
|
12
|
+
# Optional Fields Appearing Once
|
13
|
+
# The following header fields are optional and MUST NOT appear more than once:
|
14
|
+
# - "Reporting-MTA" is defined in Sisimai::RFC1894->FIELDINDEX()
|
15
|
+
'Original-Envelope-Id', 'Original-Mail-From', 'Arrival-Date', 'Source-IP', 'Incidents',
|
16
|
+
|
17
|
+
# Optional Fields Appearing Multiple Times
|
18
|
+
# The following set of header fields are optional and may appear any number of times as
|
19
|
+
# appropriate:
|
20
|
+
'Authentication-Results', 'Original-Rcpt-To', 'Reported-Domain', 'Reported-URI',
|
21
|
+
|
22
|
+
# The historic field "Received-Date" SHOULD also be accepted and interpreted identically
|
23
|
+
# to "Arrival-Date". However, if both are present, the report is malformed and SHOULD be
|
24
|
+
# treated as described in Section 4.
|
25
|
+
'Received-Date',
|
26
|
+
]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
data/lib/sisimai/rhost/cox.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
module Sisimai
|
2
2
|
module Rhost
|
3
|
-
# Sisimai::Rhost detects the bounce reason from the content of Sisimai::
|
4
|
-
#
|
5
|
-
#
|
3
|
+
# Sisimai::Rhost detects the bounce reason from the content of Sisimai::Fact object as an argument
|
4
|
+
# of get() method when the value of "destination" of the object is "charter.net". This class is
|
5
|
+
# called only Sisimai::Fact class.
|
6
6
|
module Cox
|
7
7
|
class << self
|
8
|
-
# Imported from p5-Sisimail/lib/Sisimai/Rhost/Cox.pm
|
9
8
|
ErrorCodes = {
|
10
9
|
# https://www.cox.com/residential/support/email-error-codes.html
|
11
10
|
'CXBL' => 'blocked', # The sending IP address has been blocked by Cox due to exhibiting spam-like behavior.
|
@@ -79,11 +78,11 @@ module Sisimai
|
|
79
78
|
}.freeze
|
80
79
|
|
81
80
|
# Detect bounce reason from https://cox.com/
|
82
|
-
# @param [Sisimai::
|
81
|
+
# @param [Sisimai::Fact] argvs Parsed email object
|
83
82
|
# @return [String, Nil] The bounce reason at Cox
|
84
83
|
# @since v4.25.8
|
85
84
|
def get(argvs)
|
86
|
-
statusmesg = argvs
|
85
|
+
statusmesg = argvs['diagnosticcode']
|
87
86
|
codenumber = 0
|
88
87
|
|
89
88
|
if cv = statusmesg.match(/AUP#([0-9A-Z]+)/)
|
@@ -1,12 +1,10 @@
|
|
1
1
|
module Sisimai
|
2
2
|
module Rhost
|
3
|
-
# Sisimai::Rhost detects the bounce reason from the content of Sisimai::
|
4
|
-
#
|
5
|
-
# is
|
6
|
-
# This class is called only Sisimai::Data class.
|
3
|
+
# Sisimai::Rhost detects the bounce reason from the content of Sisimai::Fact object as an argument
|
4
|
+
# of get() method when the value of "rhost" of the object is "*.laposte.net" or "*.orange.fr".
|
5
|
+
# This class is called only Sisimai::Fact class.
|
7
6
|
module FrancePTT
|
8
7
|
class << self
|
9
|
-
# Imported from p5-Sisimail/lib/Sisimai/Rhost/FrancePTT.pm
|
10
8
|
ErrorCodes = {
|
11
9
|
# 550 5.7.1 Service unavailable; client [X.X.X.X] blocked using Spamhaus
|
12
10
|
# Les emails envoyes vers la messagerie Laposte.net ont ete bloques par nos services.
|
@@ -133,12 +131,12 @@ module Sisimai
|
|
133
131
|
}.freeze
|
134
132
|
|
135
133
|
# Detect bounce reason from Oranage or La Poste
|
136
|
-
# @param [Sisimai::
|
134
|
+
# @param [Sisimai::Fact] argvs Parsed email object
|
137
135
|
# @return [String] The bounce reason for Orange or La Poste
|
138
136
|
def get(argvs)
|
139
|
-
return argvs
|
137
|
+
return argvs['reason'] unless argvs['reason'].empty?
|
140
138
|
|
141
|
-
statusmesg = argvs
|
139
|
+
statusmesg = argvs['diagnosticcode']
|
142
140
|
reasontext = ''
|
143
141
|
|
144
142
|
if cv = statusmesg.match(/\b(LPN|LPNAAA|OFR|OUK)(_[0-9]{3}|[0-9]{3}[-_][0-9]{3})\b/i)
|
@@ -1,14 +1,13 @@
|
|
1
1
|
module Sisimai
|
2
2
|
module Rhost
|
3
|
-
# Sisimai::Rhost detects the bounce reason from the content of Sisimai::
|
4
|
-
#
|
5
|
-
#
|
3
|
+
# Sisimai::Rhost detects the bounce reason from the content of Sisimai::Fact object as an argument
|
4
|
+
# of get() method when the value of "rhost" of the object is "*.secureserver.net". This class is
|
5
|
+
# called only Sisimai::Fact class.
|
6
6
|
module GoDaddy
|
7
7
|
class << self
|
8
|
-
# Imported from p5-Sisimail/lib/Sisimai/Rhost/GoDaddy.pm
|
9
8
|
# https://www.godaddy.com/help/what-does-my-email-bounceback-mean-3568
|
10
9
|
ErrorCodes = {
|
11
|
-
'IB103' => '
|
10
|
+
'IB103' => 'badreputation', # 554 Connection refused. This IP has a poor reputation on Cloudmark Sender Intelligence (CSI). IB103
|
12
11
|
'IB104' => 'blocked', # 554 Connection refused. This IP is listed on the Spamhaus Block List (SBL). IB104
|
13
12
|
'IB105' => 'blocked', # 554 Connection refused. This IP is listed on the Exploits Block List (XBL). IB105
|
14
13
|
'IB106' => 'blocked', # 554 Connection refused. This IP is listed on the Policy Block List (PBL). IB106
|
@@ -39,19 +38,19 @@ module Sisimai
|
|
39
38
|
}.freeze
|
40
39
|
|
41
40
|
# Detect bounce reason from GoDaddy
|
42
|
-
# @param [Sisimai::
|
41
|
+
# @param [Sisimai::Fact] argvs Parsed email object
|
43
42
|
# @return [String] The bounce reason for GoDaddy
|
44
43
|
# @see https://www.godaddy.com/help/what-does-my-email-bounceback-mean-3568
|
45
44
|
def get(argvs)
|
46
|
-
return argvs
|
45
|
+
return argvs['reason'] unless argvs['reason'].empty?
|
47
46
|
|
48
|
-
statusmesg = argvs
|
47
|
+
statusmesg = argvs['diagnosticcode']
|
48
|
+
positionib = statusmesg.index(' IB') || -1
|
49
49
|
reasontext = ''
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
else
|
51
|
+
# 192.0.2.22 has sent to too many recipients this hour. IB607 ...
|
52
|
+
reasontext = ErrorCodes[statusmesg[positionib + 1, 5]] || '' if positionib > 1
|
53
|
+
if reasontext.empty?
|
55
54
|
# 553 http://www.spamhaus.org/query/bl?ip=192.0.0.222
|
56
55
|
MessagesOf.each_key do |e|
|
57
56
|
MessagesOf[e].each do |f|
|
@@ -62,6 +61,7 @@ module Sisimai
|
|
62
61
|
break unless reasontext.empty?
|
63
62
|
end
|
64
63
|
end
|
64
|
+
|
65
65
|
return reasontext
|
66
66
|
end
|
67
67
|
|