sisimai 4.25.16-java → 5.0.2-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/.github/workflows/rake-test.yml +55 -0
- data/.travis.yml +3 -3
- data/ANALYTICAL-PRECISION +2 -2
- data/Benchmarks.mk +3 -3
- data/CONTRIBUTING +1 -1
- data/ChangeLog.md +451 -393
- data/Developers.mk +5 -6
- data/Gemfile +1 -1
- data/Makefile +15 -15
- data/README-JA.md +323 -149
- data/README.md +319 -149
- 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 +506 -0
- data/lib/sisimai/lhost/activehunter.rb +12 -14
- data/lib/sisimai/lhost/amavis.rb +11 -14
- data/lib/sisimai/lhost/amazonses.rb +37 -42
- data/lib/sisimai/lhost/amazonworkmail.rb +15 -19
- data/lib/sisimai/lhost/aol.rb +12 -15
- data/lib/sisimai/lhost/apachejames.rb +19 -21
- data/lib/sisimai/lhost/barracuda.rb +10 -12
- data/lib/sisimai/lhost/bigfoot.rb +21 -22
- data/lib/sisimai/lhost/biglobe.rb +15 -16
- data/lib/sisimai/lhost/courier.rb +20 -20
- data/lib/sisimai/lhost/domino.rb +23 -20
- 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 +179 -174
- data/lib/sisimai/lhost/ezweb.rb +31 -56
- data/lib/sisimai/lhost/facebook.rb +21 -34
- 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 +11 -11
- data/lib/sisimai/lhost/gsuite.rb +21 -28
- 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 +37 -40
- data/lib/sisimai/lhost/mcafee.rb +21 -31
- data/lib/sisimai/lhost/messagelabs.rb +17 -21
- data/lib/sisimai/lhost/messagingserver.rb +40 -37
- data/lib/sisimai/lhost/mfilter.rb +16 -17
- data/lib/sisimai/lhost/mxlogic.rb +24 -33
- data/lib/sisimai/lhost/notes.rb +17 -17
- data/lib/sisimai/lhost/office365.rb +64 -28
- data/lib/sisimai/lhost/opensmtpd.rb +12 -13
- data/lib/sisimai/lhost/outlook.rb +12 -16
- data/lib/sisimai/lhost/postfix.rb +179 -130
- data/lib/sisimai/lhost/powermta.rb +12 -14
- data/lib/sisimai/lhost/qmail.rb +44 -47
- data/lib/sisimai/lhost/receivingses.rb +15 -21
- data/lib/sisimai/lhost/sendgrid.rb +34 -34
- data/lib/sisimai/lhost/sendmail.rb +65 -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 -21
- 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 +444 -326
- data/lib/sisimai/order.rb +5 -5
- data/lib/sisimai/reason/authfailure.rb +65 -0
- data/lib/sisimai/reason/badreputation.rb +53 -0
- data/lib/sisimai/reason/blocked.rb +96 -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 +7 -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 +11 -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 +34 -36
- data/lib/sisimai/reason/spamdetected.rb +115 -147
- data/lib/sisimai/reason/speeding.rb +49 -0
- data/lib/sisimai/reason/suspend.rb +12 -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 +177 -146
- 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/google.rb +530 -0
- 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 +51 -42
- 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 +507 -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 +46 -31
- 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/lhost-sendmail-60.eml +85 -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 +48 -26
- 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/lib/sisimai/rhost/googleapps.rb +0 -261
- /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/lhost/qmail.rb
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
module Sisimai::Lhost
|
|
2
|
-
# Sisimai::Lhost::Qmail parses a bounce email which created by qmail.
|
|
3
|
-
#
|
|
2
|
+
# Sisimai::Lhost::Qmail parses a bounce email which created by qmail. Methods in the module are called
|
|
3
|
+
# from only Sisimai::Message.
|
|
4
4
|
module Qmail
|
|
5
5
|
class << self
|
|
6
|
-
# Imported from p5-Sisimail/lib/Sisimai/Lhost/qmail.pm
|
|
7
6
|
require 'sisimai/lhost'
|
|
8
7
|
|
|
9
8
|
Indicators = Sisimai::Lhost.INDICATORS
|
|
10
|
-
|
|
9
|
+
Boundaries = ['--- Below this line is a copy of the message.'].freeze
|
|
11
10
|
StartingOf = {
|
|
12
11
|
# qmail-remote.c:248| if (code >= 500) {
|
|
13
12
|
# qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
|
|
@@ -16,45 +15,33 @@ module Sisimai::Lhost
|
|
|
16
15
|
#
|
|
17
16
|
# Characters: K,Z,D in qmail-qmqpc.c, qmail-send.c, qmail-rspawn.c
|
|
18
17
|
# K = success, Z = temporary error, D = permanent error
|
|
19
|
-
message: ['Hi. This is the qmail'],
|
|
20
18
|
error: ['Remote host said:'],
|
|
19
|
+
message: ['Hi. This is the qmail'],
|
|
20
|
+
rhost: ['Giving up on ', 'Connected to ', 'remote host '],
|
|
21
21
|
}.freeze
|
|
22
|
-
|
|
23
|
-
ReSMTP = {
|
|
22
|
+
CommandSet = {
|
|
24
23
|
# Error text regular expressions which defined in qmail-remote.c
|
|
25
24
|
# qmail-remote.c:225| if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
|
|
26
|
-
'conn' =>
|
|
25
|
+
'conn' => [' but greeting failed.'],
|
|
27
26
|
# qmail-remote.c:231| if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
|
|
28
|
-
'ehlo' =>
|
|
27
|
+
'ehlo' => [' but my name was rejected.'],
|
|
29
28
|
# qmail-remote.c:238| if (code >= 500) quit("DConnected to "," but sender was rejected");
|
|
30
29
|
# reason = rejected
|
|
31
|
-
'mail' =>
|
|
30
|
+
'mail' => [' but sender was rejected.'],
|
|
32
31
|
# qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
|
|
33
32
|
# qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
|
|
34
33
|
# reason = userunknown
|
|
35
|
-
'rcpt' =>
|
|
34
|
+
'rcpt' => [' does not like recipient.'],
|
|
36
35
|
# qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
|
|
37
36
|
# qmail-remote.c:266| if (code >= 400) quit("Z"," failed on DATA command");
|
|
38
37
|
# qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
|
|
39
38
|
# qmail-remote.c:272| if (code >= 400) quit("Z"," failed after I sent the message");
|
|
40
|
-
'data' =>
|
|
41
|
-
(?:Error:)?[^ ]+[ ]failed[ ]on[ ]DATA[ ]command[.]
|
|
42
|
-
|(?:Error:)?[^ ]+[ ]failed[ ]after[ ]I[ ]sent[ ]the[ ]message[.]
|
|
43
|
-
)
|
|
44
|
-
}x,
|
|
39
|
+
'data' => [' failed on DATA command', ' failed after I sent the message'],
|
|
45
40
|
}.freeze
|
|
46
|
-
# qmail-remote.c:261| if (!flagbother) quit("DGiving up on ","");
|
|
47
|
-
ReHost = %r{(?:
|
|
48
|
-
Giving[ ]up[ ]on[ ]([^ ]+[0-9a-zA-Z])[.]?\z
|
|
49
|
-
|Connected[ ]to[ ]([-0-9a-zA-Z.]+[0-9a-zA-Z])[ ]
|
|
50
|
-
|remote[ ]host[ ]([-0-9a-zA-Z.]+[0-9a-zA-Z])[ ]said:
|
|
51
|
-
)
|
|
52
|
-
}x
|
|
53
41
|
|
|
54
42
|
# qmail-send.c:922| ... (&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
|
|
55
43
|
HasExpired = 'this message has been in the queue too long.'
|
|
56
|
-
|
|
57
|
-
ReIsOnHold = %r/\A[^ ]+ does not like recipient[.][ ]+.+this message has been in the queue too long[.]\z/
|
|
44
|
+
OnHoldPair = [' does not like recipient.', 'this message has been in the queue too long.'].freeze
|
|
58
45
|
FailOnLDAP = {
|
|
59
46
|
# qmail-ldap-1.03-20040101.patch:19817 - 19866
|
|
60
47
|
'suspend' => ['Mailaddress is administrative?le?y disabled'], # 5.2.1
|
|
@@ -104,27 +91,31 @@ module Sisimai::Lhost
|
|
|
104
91
|
# @param [String] mbody Message body of a bounce email
|
|
105
92
|
# @return [Hash] Bounce data list and message/rfc822 part
|
|
106
93
|
# @return [Nil] it failed to parse or the arguments are missing
|
|
107
|
-
def
|
|
94
|
+
def inquire(mhead, mbody)
|
|
108
95
|
# Pre process email headers and the body part of the message which generated
|
|
109
96
|
# by qmail, see https://cr.yp.to/qmail.html
|
|
110
97
|
# e.g.) Received: (qmail 12345 invoked for bounce); 29 Apr 2009 12:34:56 -0000
|
|
111
98
|
# Subject: failure notice
|
|
112
|
-
tryto =
|
|
99
|
+
tryto = [['(qmail ', 'invoked for bounce)'], ['(qmail ', 'invoked from ', 'network)']]
|
|
113
100
|
match = 0
|
|
114
101
|
match += 1 if mhead['subject'] == 'failure notice'
|
|
115
|
-
|
|
102
|
+
mhead['received'].each do |e|
|
|
103
|
+
# Received: (qmail 2222 invoked for bounce);29 Apr 2017 23:34:45 +0900
|
|
104
|
+
# Received: (qmail 2202 invoked from network); 29 Apr 2018 00:00:00 +0900
|
|
105
|
+
match += 1 if tryto.any? { |a| Sisimai::String.aligned(e, a) }
|
|
106
|
+
end
|
|
116
107
|
return nil unless match > 0
|
|
117
108
|
|
|
118
109
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
|
119
|
-
|
|
120
|
-
bodyslices =
|
|
110
|
+
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
|
111
|
+
bodyslices = emailparts[0].split("\n")
|
|
121
112
|
readcursor = 0 # (Integer) Points the current cursor position
|
|
122
113
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
|
123
114
|
v = nil
|
|
124
115
|
|
|
125
116
|
while e = bodyslices.shift do
|
|
126
|
-
# Read error messages and delivery status lines from the head of the email
|
|
127
|
-
#
|
|
117
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
|
118
|
+
# line of the beginning of the original message.
|
|
128
119
|
if readcursor == 0
|
|
129
120
|
# Beginning of the bounce message or delivery status part
|
|
130
121
|
readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
|
|
@@ -139,14 +130,14 @@ module Sisimai::Lhost
|
|
|
139
130
|
# Giving up on 192.0.2.153.
|
|
140
131
|
v = dscontents[-1]
|
|
141
132
|
|
|
142
|
-
if
|
|
133
|
+
if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>', ':'])
|
|
143
134
|
# <kijitora@example.jp>:
|
|
144
135
|
if v['recipient']
|
|
145
136
|
# There are multiple recipient addresses in the message body.
|
|
146
137
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
|
147
138
|
v = dscontents[-1]
|
|
148
139
|
end
|
|
149
|
-
v['recipient'] =
|
|
140
|
+
v['recipient'] = Sisimai::Address.s3s4(e[e.index('<'), e.size])
|
|
150
141
|
recipients += 1
|
|
151
142
|
|
|
152
143
|
elsif dscontents.size == recipients
|
|
@@ -157,8 +148,15 @@ module Sisimai::Lhost
|
|
|
157
148
|
v['alterrors'] = e if e.start_with?(StartingOf[:error][0])
|
|
158
149
|
|
|
159
150
|
next if v['rhost']
|
|
160
|
-
|
|
161
|
-
|
|
151
|
+
StartingOf[:rhost].each do |r|
|
|
152
|
+
# Find a remote host name
|
|
153
|
+
p1 = e.index(r); next unless p1
|
|
154
|
+
cm = r.size
|
|
155
|
+
p2 = e.index(' ', p1 + cm + 1) || p2 = e.rindex('.')
|
|
156
|
+
|
|
157
|
+
v['rhost'] = e[p1 + cm, p2 - p1 - cm]
|
|
158
|
+
break
|
|
159
|
+
end
|
|
162
160
|
end
|
|
163
161
|
end
|
|
164
162
|
return nil unless recipients > 0
|
|
@@ -169,17 +167,16 @@ module Sisimai::Lhost
|
|
|
169
167
|
|
|
170
168
|
unless e['command']
|
|
171
169
|
# Get the SMTP command name for the session
|
|
172
|
-
|
|
170
|
+
CommandSet.each_key do |r|
|
|
173
171
|
# Verify each regular expression of SMTP commands
|
|
174
|
-
next unless e['diagnosis']
|
|
172
|
+
next unless CommandSet[r].any? { |a| e['diagnosis'].include?(a) }
|
|
175
173
|
e['command'] = r.upcase
|
|
176
174
|
break
|
|
177
175
|
end
|
|
178
176
|
|
|
179
|
-
|
|
180
|
-
#
|
|
181
|
-
|
|
182
|
-
e['command'] ||= ''
|
|
177
|
+
if e['diagnosis'].include?('Sorry, no SMTP connection got far enough; most progress was ')
|
|
178
|
+
# Get the last SMTP command:from the error message
|
|
179
|
+
e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis']) || ''
|
|
183
180
|
end
|
|
184
181
|
end
|
|
185
182
|
|
|
@@ -193,9 +190,8 @@ module Sisimai::Lhost
|
|
|
193
190
|
e['reason'] = 'blocked'
|
|
194
191
|
else
|
|
195
192
|
# Try to match with each error message in the table
|
|
196
|
-
if e['diagnosis']
|
|
197
|
-
# To decide the reason require pattern match with
|
|
198
|
-
# Sisimai::Reason::* modules
|
|
193
|
+
if Sisimai::String.aligned(e['diagnosis'], OnHoldPair)
|
|
194
|
+
# To decide the reason require pattern match with Sisimai::Reason::* modules
|
|
199
195
|
e['reason'] = 'onhold'
|
|
200
196
|
else
|
|
201
197
|
MessagesOf.each_key do |r|
|
|
@@ -227,10 +223,11 @@ module Sisimai::Lhost
|
|
|
227
223
|
end
|
|
228
224
|
end
|
|
229
225
|
|
|
230
|
-
e['
|
|
226
|
+
e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis'])
|
|
227
|
+
e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
|
|
231
228
|
end
|
|
232
229
|
|
|
233
|
-
return { 'ds' => dscontents, 'rfc822' =>
|
|
230
|
+
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
|
234
231
|
end
|
|
235
232
|
def description; return 'qmail'; end
|
|
236
233
|
end
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
module Sisimai::Lhost
|
|
2
|
-
# Sisimai::Lhost::ReceivingSES parses a bounce email which created
|
|
3
|
-
#
|
|
4
|
-
# only Sisimai::Message.
|
|
2
|
+
# Sisimai::Lhost::ReceivingSES parses a bounce email which created by Amazon Simple Email Service.
|
|
3
|
+
# Methods in the module are called from only Sisimai::Message.
|
|
5
4
|
module ReceivingSES
|
|
6
5
|
class << self
|
|
7
|
-
# Imported from p5-Sisimail/lib/Sisimai/Lhost/ReceivingSES.pm
|
|
8
6
|
require 'sisimai/lhost'
|
|
9
7
|
|
|
10
8
|
# https://aws.amazon.com/ses/
|
|
11
9
|
Indicators = Sisimai::Lhost.INDICATORS
|
|
12
|
-
|
|
10
|
+
Boundaries = ['Content-Type: text/rfc822-headers'].freeze
|
|
13
11
|
StartingOf = { message: ['This message could not be delivered.'] }.freeze
|
|
14
12
|
MessagesOf = {
|
|
15
13
|
# The followings are error messages in Rule sets/*/Actions/Template
|
|
@@ -24,26 +22,25 @@ module Sisimai::Lhost
|
|
|
24
22
|
# @param [String] mbody Message body of a bounce email
|
|
25
23
|
# @return [Hash] Bounce data list and message/rfc822 part
|
|
26
24
|
# @return [Nil] it failed to parse or the arguments are missing
|
|
27
|
-
def
|
|
25
|
+
def inquire(mhead, mbody)
|
|
28
26
|
# X-SES-Outgoing: 2015.10.01-54.240.27.7
|
|
29
27
|
# Feedback-ID: 1.us-west-2.HX6/J9OVlHTadQhEu1+wdF9DBj6n6Pa9sW5Y/0pSOi8=:AmazonSES
|
|
30
28
|
return nil unless mhead['x-ses-outgoing']
|
|
31
29
|
|
|
32
|
-
require 'sisimai/rfc1894'
|
|
33
30
|
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
|
34
31
|
permessage = {} # (Hash) Store values of each Per-Message field
|
|
35
32
|
|
|
36
33
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
|
37
|
-
|
|
38
|
-
bodyslices =
|
|
34
|
+
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
|
35
|
+
bodyslices = emailparts[0].split("\n")
|
|
39
36
|
readslices = ['']
|
|
40
37
|
readcursor = 0 # (Integer) Points the current cursor position
|
|
41
38
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
|
42
39
|
v = nil
|
|
43
40
|
|
|
44
41
|
while e = bodyslices.shift do
|
|
45
|
-
# Read error messages and delivery status lines from the head of the email
|
|
46
|
-
#
|
|
42
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
|
43
|
+
# line of the beginning of the original message.
|
|
47
44
|
readslices << e # Save the current line for the next loop
|
|
48
45
|
|
|
49
46
|
if readcursor == 0
|
|
@@ -84,14 +81,14 @@ module Sisimai::Lhost
|
|
|
84
81
|
next unless fieldtable[o[0]]
|
|
85
82
|
v[fieldtable[o[0]]] = o[2]
|
|
86
83
|
|
|
87
|
-
next unless f
|
|
84
|
+
next unless f
|
|
88
85
|
permessage[fieldtable[o[0]]] = o[2]
|
|
89
86
|
end
|
|
90
87
|
else
|
|
91
88
|
# Continued line of the value of Diagnostic-Code field
|
|
92
89
|
next unless readslices[-2].start_with?('Diagnostic-Code:')
|
|
93
|
-
next unless
|
|
94
|
-
v['diagnosis'] << ' ' <<
|
|
90
|
+
next unless e.start_with?(' ')
|
|
91
|
+
v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
|
|
95
92
|
readslices[-1] = 'Diagnostic-Code: ' << e
|
|
96
93
|
end
|
|
97
94
|
end
|
|
@@ -99,18 +96,15 @@ module Sisimai::Lhost
|
|
|
99
96
|
|
|
100
97
|
dscontents.each do |e|
|
|
101
98
|
# Set default values if each value is empty.
|
|
102
|
-
e['lhost'] ||= permessage['rhost']
|
|
103
99
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
|
104
100
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].tr("\n", ' '))
|
|
105
101
|
|
|
106
102
|
if e['status'].to_s.start_with?('5.0.0', '5.1.0', '4.0.0', '4.1.0')
|
|
107
103
|
# Get other D.S.N. value from the error message
|
|
108
104
|
errormessage = e['diagnosis']
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
errormessage = cv[1]
|
|
113
|
-
end
|
|
105
|
+
p1 = e['diagnosis'].index("-'"); p1 = e['diagnosis'].index('-"') unless p1
|
|
106
|
+
p2 = e['diagnosis'].rindex("' "); p2 = e['diagnosis'].rindex('" ') unless p2
|
|
107
|
+
errormessage = e['diagnosis'][p1 + 2, p2 - p1 - 2] if p1 && p2
|
|
114
108
|
e['status'] = Sisimai::SMTP::Status.find(errormessage) || e['status']
|
|
115
109
|
end
|
|
116
110
|
|
|
@@ -123,7 +117,7 @@ module Sisimai::Lhost
|
|
|
123
117
|
e['reason'] ||= Sisimai::SMTP::Status.name(e['status']) || ''
|
|
124
118
|
end
|
|
125
119
|
|
|
126
|
-
return { 'ds' => dscontents, 'rfc822' =>
|
|
120
|
+
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
|
127
121
|
end
|
|
128
122
|
def description; return 'Amazon SES(Receiving): https://aws.amazon.com/ses/'; end
|
|
129
123
|
end
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
module Sisimai::Lhost
|
|
2
|
-
# Sisimai::Lhost::SendGrid parses a bounce email which created by
|
|
3
|
-
#
|
|
2
|
+
# Sisimai::Lhost::SendGrid parses a bounce email which created by SendGrid. Methods in the module
|
|
3
|
+
# are called from only Sisimai::Message.
|
|
4
4
|
module SendGrid
|
|
5
5
|
class << self
|
|
6
|
-
# Imported from p5-Sisimail/lib/Sisimai/Lhost/SendGrid.pm
|
|
7
6
|
require 'sisimai/lhost'
|
|
8
7
|
|
|
9
8
|
Indicators = Sisimai::Lhost.INDICATORS
|
|
10
|
-
|
|
9
|
+
Boundaries = ['Content-Type: message/rfc822'].freeze
|
|
11
10
|
StartingOf = { message: ['This is an automatically generated message from SendGrid.'] }.freeze
|
|
12
11
|
|
|
13
12
|
# Parse bounce messages from SendGrid
|
|
@@ -15,29 +14,29 @@ module Sisimai::Lhost
|
|
|
15
14
|
# @param [String] mbody Message body of a bounce email
|
|
16
15
|
# @return [Hash] Bounce data list and message/rfc822 part
|
|
17
16
|
# @return [Nil] it failed to parse or the arguments are missing
|
|
18
|
-
def
|
|
17
|
+
def inquire(mhead, mbody)
|
|
19
18
|
# Return-Path: <apps@sendgrid.net>
|
|
20
19
|
# X-Mailer: MIME-tools 5.502 (Entity 5.502)
|
|
21
20
|
return nil unless mhead['return-path']
|
|
22
21
|
return nil unless mhead['return-path'] == '<apps@sendgrid.net>'
|
|
23
22
|
return nil unless mhead['subject'] == 'Undelivered Mail Returned to Sender'
|
|
24
23
|
|
|
25
|
-
require 'sisimai/
|
|
24
|
+
require 'sisimai/smtp/command'
|
|
26
25
|
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
|
27
26
|
permessage = {} # (Hash) Store values of each Per-Message field
|
|
28
27
|
|
|
29
28
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
|
30
|
-
|
|
31
|
-
bodyslices =
|
|
29
|
+
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
|
30
|
+
bodyslices = emailparts[0].split("\n")
|
|
32
31
|
readslices = ['']
|
|
33
32
|
readcursor = 0 # (Integer) Points the current cursor position
|
|
34
33
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
|
35
|
-
|
|
34
|
+
thecommand = '' # (String) SMTP Command name begin with the string '>>>'
|
|
36
35
|
v = nil
|
|
37
36
|
|
|
38
37
|
while e = bodyslices.shift do
|
|
39
|
-
# Read error messages and delivery status lines from the head of the email
|
|
40
|
-
#
|
|
38
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
|
39
|
+
# line of the beginning of the original message.
|
|
41
40
|
readslices << e # Save the current line for the next loop
|
|
42
41
|
|
|
43
42
|
if readcursor == 0
|
|
@@ -57,8 +56,7 @@ module Sisimai::Lhost
|
|
|
57
56
|
# Fallback code for empty value or invalid formatted value
|
|
58
57
|
# - Status: (empty)
|
|
59
58
|
# - Diagnostic-Code: 550 5.1.1 ... (No "diagnostic-type" sub field)
|
|
60
|
-
|
|
61
|
-
v['diagnosis'] = cv[1]
|
|
59
|
+
v['diagnosis'] = e[e.index(':') + 2, e.size] if e.start_with?('Diagnostic-Code: ')
|
|
62
60
|
next
|
|
63
61
|
end
|
|
64
62
|
|
|
@@ -84,29 +82,37 @@ module Sisimai::Lhost
|
|
|
84
82
|
v['diagnosis'] = o[2]
|
|
85
83
|
elsif o[-1] == 'date'
|
|
86
84
|
# Arrival-Date: 2012-12-31 23-59-59
|
|
87
|
-
next unless
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
next unless e.start_with?('Arrival-Date: ')
|
|
86
|
+
cf = e[e.index(': ') + 2, e.size].split(' '); next unless cf.size == 2
|
|
87
|
+
cw = cf[0].split('-'); next unless cw.size == 3
|
|
88
|
+
ce = cf[1].split('-'); next unless ce.size == 3
|
|
89
|
+
|
|
90
|
+
o[1] << 'Thu, ' << cw[2] + ' '
|
|
91
|
+
o[1] << Sisimai::DateTime.monthname(false)[cw[1].to_i - 1]
|
|
92
|
+
o[1] << ' ' << cw[0] + ' ' << ce.join(':')
|
|
91
93
|
o[1] << ' ' << Sisimai::DateTime.abbr2tz('CDT')
|
|
92
94
|
else
|
|
93
95
|
# Other DSN fields defined in RFC3464
|
|
94
96
|
next unless fieldtable[o[0]]
|
|
95
97
|
v[fieldtable[o[0]]] = o[2]
|
|
96
98
|
|
|
97
|
-
next unless f
|
|
99
|
+
next unless f
|
|
98
100
|
permessage[fieldtable[o[0]]] = o[2]
|
|
99
101
|
end
|
|
100
102
|
else
|
|
101
103
|
# The line does not begin with a DSN field defined in RFC3464
|
|
102
|
-
if cv =
|
|
104
|
+
if cv = Sisimai::SMTP::Command.find(e)
|
|
103
105
|
# in RCPT TO, in MAIL FROM, end of DATA
|
|
104
|
-
|
|
106
|
+
thecommand = cv
|
|
107
|
+
elsif e.start_with?('Diagnostic-Code: ')
|
|
108
|
+
# Diagnostic-Code: 550 5.1.1 <kijitora@example.jp>... User Unknown
|
|
109
|
+
v['diagnosis'] = e[e.index(':') + 2, e.size]
|
|
105
110
|
else
|
|
106
111
|
# Continued line of the value of Diagnostic-Code field
|
|
107
112
|
next unless readslices[-2].start_with?('Diagnostic-Code:')
|
|
108
|
-
next unless
|
|
109
|
-
v['diagnosis']
|
|
113
|
+
next unless e.start_with?(' ')
|
|
114
|
+
v['diagnosis'] ||= ''
|
|
115
|
+
v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
|
|
110
116
|
readslices[-1] = 'Diagnostic-Code: ' << e
|
|
111
117
|
end
|
|
112
118
|
end
|
|
@@ -116,14 +122,12 @@ module Sisimai::Lhost
|
|
|
116
122
|
dscontents.each do |e|
|
|
117
123
|
# Get the value of SMTP status code as a pseudo D.S.N.
|
|
118
124
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
end
|
|
125
|
+
e['replycode'] = Sisimai::SMTP::Reply.find(e['diagnosis']) || ''
|
|
126
|
+
e['status'] = e['replycode'][0, 1] + '.0.0' if e['replycode'].size == 3
|
|
127
|
+
e['command'] = thecommand
|
|
123
128
|
|
|
124
129
|
if e['status'] == '5.0.0' || e['status'] == '4.0.0'
|
|
125
|
-
# Get the value of D.S.N. from the error message or the value of
|
|
126
|
-
# Diagnostic-Code header.
|
|
130
|
+
# Get the value of D.S.N. from the error message or the value of Diagnostic-Code header.
|
|
127
131
|
e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || e['status']
|
|
128
132
|
end
|
|
129
133
|
|
|
@@ -131,17 +135,13 @@ module Sisimai::Lhost
|
|
|
131
135
|
# Action: expired
|
|
132
136
|
e['reason'] = 'expired'
|
|
133
137
|
if !e['status'] || e['status'].end_with?('.0.0')
|
|
134
|
-
# Set pseudo Status code value if the value of Status is not
|
|
135
|
-
# defined or 4.0.0 or 5.0.0.
|
|
138
|
+
# Set pseudo Status code value if the value of Status is not defined or 4.0.0 or 5.0.0.
|
|
136
139
|
e['status'] = Sisimai::SMTP::Status.code('expired') || e['status']
|
|
137
140
|
end
|
|
138
141
|
end
|
|
139
|
-
|
|
140
|
-
e['lhost'] ||= permessage['rhost']
|
|
141
|
-
e['command'] = commandtxt
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
-
return { 'ds' => dscontents, 'rfc822' =>
|
|
144
|
+
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
|
145
145
|
end
|
|
146
146
|
def description; return 'SendGrid: https://sendgrid.com/'; end
|
|
147
147
|
end
|