sisimai 4.25.15 → 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 +419 -388
- 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 -313
- 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 -47
- 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/lhost/exim.rb
CHANGED
@@ -1,24 +1,21 @@
|
|
1
1
|
module Sisimai::Lhost
|
2
|
-
# Sisimai::Lhost::Exim parses a bounce email which created by Exim.
|
3
|
-
#
|
2
|
+
# Sisimai::Lhost::Exim parses a bounce email which created by Exim. Methods in the module are called
|
3
|
+
# from only Sisimai::Message.
|
4
4
|
module Exim
|
5
5
|
class << self
|
6
|
-
# Imported from p5-Sisimail/lib/Sisimai/Lhost/Exim.pm
|
7
6
|
require 'sisimai/lhost'
|
8
7
|
|
9
8
|
Indicators = Sisimai::Lhost.INDICATORS
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
MarkingsOf = {
|
21
|
-
# Error text regular expressions which defined in exim/src/deliver.c
|
9
|
+
Boundaries = [
|
10
|
+
# deliver.c:6423| if (bounce_return_body) fprintf(f,
|
11
|
+
# deliver.c:6424|"------ This is a copy of the message, including all the headers. ------\n");
|
12
|
+
# deliver.c:6425| else fprintf(f,
|
13
|
+
# deliver.c:6426|"------ This is a copy of the message's headers. ------\n");
|
14
|
+
'------ This is a copy of the message, including all the headers. ------',
|
15
|
+
'Content-Type: message/rfc822',
|
16
|
+
].freeze
|
17
|
+
StartingOf = {
|
18
|
+
# Error text strings which defined in exim/src/deliver.c
|
22
19
|
#
|
23
20
|
# deliver.c:6292| fprintf(f,
|
24
21
|
# deliver.c:6293|"This message was created automatically by mail delivery software.\n");
|
@@ -35,19 +32,19 @@ module Sisimai::Lhost
|
|
35
32
|
# deliver.c:6304|"could not be delivered to one or more of its recipients. The following\n"
|
36
33
|
# deliver.c:6305|"address(es) failed:\n", sender_address);
|
37
34
|
# deliver.c:6306| }
|
38
|
-
|
39
|
-
frozen:
|
40
|
-
message:
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
)
|
48
|
-
|
35
|
+
deliverystatus: ['Content-Type: message/delivery-status'],
|
36
|
+
frozen: [' has been frozen', ' was frozen on arrival'],
|
37
|
+
message: [
|
38
|
+
'This message was created automatically by mail delivery software.',
|
39
|
+
'A message that you sent was rejected by the local scannning code',
|
40
|
+
'A message that you sent contained one or more recipient addresses ',
|
41
|
+
'A message that you sent could not be delivered to all of its recipients',
|
42
|
+
' has been frozen',
|
43
|
+
' was frozen on arrival',
|
44
|
+
' router encountered the following error(s):',
|
45
|
+
],
|
49
46
|
}.freeze
|
50
|
-
|
47
|
+
MarkingsOf = { alias: ' an undisclosed address' }.freeze
|
51
48
|
ReCommands = [
|
52
49
|
# transports/smtp.c:564| *message = US string_sprintf("SMTP error from remote mail server after %s%s: "
|
53
50
|
# transports/smtp.c:837| string_sprintf("SMTP error from remote mail server after RCPT TO:<%s>: "
|
@@ -125,30 +122,28 @@ module Sisimai::Lhost
|
|
125
122
|
# @param [String] mbody Message body of a bounce email
|
126
123
|
# @return [Hash] Bounce data list and message/rfc822 part
|
127
124
|
# @return [Nil] it failed to parse or the arguments are missing
|
128
|
-
def
|
129
|
-
return nil if mhead['from']
|
125
|
+
def inquire(mhead, mbody)
|
126
|
+
return nil if mhead['from'].include?('.mail.ru')
|
130
127
|
|
131
128
|
# Message-Id: <E1P1YNN-0003AD-Ga@example.org>
|
132
129
|
# X-Failed-Recipients: kijitora@example.ed.jp
|
133
130
|
match = 0
|
131
|
+
msgid = mhead['message-id'] || ''
|
134
132
|
match += 1 if mhead['from'].start_with?('Mail Delivery System')
|
135
|
-
match += 1 if
|
136
|
-
match += 1 if
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
)
|
144
|
-
}x
|
133
|
+
match += 1 if msgid.start_with?('<') && msgid.index('-') == 8 && msgid.index('@') == 18
|
134
|
+
match += 1 if %w[
|
135
|
+
'Delivery Status Notification',
|
136
|
+
'Mail delivery failed',
|
137
|
+
'Mail failure',
|
138
|
+
'Message frozen',
|
139
|
+
'Warning: message ',
|
140
|
+
'error(s) in forwarding or filtering'].any? { |a| mhead['subject'].include?(a) }
|
145
141
|
return nil if match < 2
|
146
142
|
|
147
|
-
require 'sisimai/rfc1894'
|
148
143
|
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
149
144
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
150
|
-
|
151
|
-
bodyslices =
|
145
|
+
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
146
|
+
bodyslices = emailparts[0].split("\n")
|
152
147
|
readcursor = 0 # (Integer) Points the current cursor position
|
153
148
|
nextcursor = 0
|
154
149
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
@@ -157,19 +152,18 @@ module Sisimai::Lhost
|
|
157
152
|
v = nil
|
158
153
|
|
159
154
|
if mhead['content-type']
|
160
|
-
# Get the boundary string and set regular expression for matching with
|
161
|
-
|
162
|
-
boundary00 = Sisimai::MIME.boundary(mhead['content-type']) || ''
|
155
|
+
# Get the boundary string and set regular expression for matching with the boundary string.
|
156
|
+
boundary00 = Sisimai::RFC2045.boundary(mhead['content-type']) || ''
|
163
157
|
end
|
164
158
|
|
165
159
|
while e = bodyslices.shift do
|
166
|
-
# Read error messages and delivery status lines from the head of the email
|
167
|
-
#
|
160
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
161
|
+
# line of the beginning of the original message.
|
168
162
|
if readcursor == 0
|
169
163
|
# Beginning of the bounce message or message/delivery-status part
|
170
|
-
if
|
164
|
+
if StartingOf[:message].any? { |a| e.include?(a) }
|
171
165
|
readcursor |= Indicators[:deliverystatus]
|
172
|
-
next unless
|
166
|
+
next unless StartingOf[:frozen].any? { |a| e.include?(a) }
|
173
167
|
end
|
174
168
|
end
|
175
169
|
next if (readcursor & Indicators[:deliverystatus]) == 0
|
@@ -185,109 +179,130 @@ module Sisimai::Lhost
|
|
185
179
|
# host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
|
186
180
|
v = dscontents[-1]
|
187
181
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
#
|
192
|
-
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
182
|
+
cv = ''
|
183
|
+
ce = false
|
184
|
+
while true
|
185
|
+
# Check if the line matche the following patterns:
|
186
|
+
break unless e.start_with?(' ') # The line should start with " " (2 spaces)
|
187
|
+
break unless e.include?('@') # "@" should be included (email)
|
188
|
+
break unless e.include?('.') # "." should be included (domain part)
|
189
|
+
break unless e.include?('pipe to |') == false # Exclude "pipe to /path/to/prog" line
|
190
|
+
|
191
|
+
break unless e[2, 1] != ' '
|
192
|
+
break unless e[2, 1] != '<'
|
193
|
+
ce = true
|
194
|
+
break
|
195
|
+
end
|
199
196
|
|
197
|
+
if ce || e.include?(MarkingsOf[:alias])
|
198
|
+
# The line is including an email address
|
200
199
|
if v['recipient']
|
201
200
|
# There are multiple recipient addresses in the message body.
|
202
201
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
203
202
|
v = dscontents[-1]
|
204
203
|
end
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
|
212
|
-
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
|
217
|
-
|
204
|
+
|
205
|
+
if e.include?(MarkingsOf[:alias])
|
206
|
+
# The line does not include an email address
|
207
|
+
# deliver.c:4549| printed = US"an undisclosed address";
|
208
|
+
# an undisclosed address
|
209
|
+
# (generated from kijitora@example.jp)
|
210
|
+
cv = e[2, e.size]
|
211
|
+
else
|
212
|
+
# kijitora@example.jp
|
213
|
+
# sabineko@example.jp: forced freeze
|
214
|
+
# mikeneko@example.jp <nekochan@example.org>: ...
|
215
|
+
p1 = e.index(' <') || -1
|
216
|
+
p2 = e.index('>:') || -1
|
217
|
+
|
218
|
+
if p1 > 1 && p2 > 1
|
219
|
+
# There are an email address and an error message in the line
|
220
|
+
# parser.c:743| while (bracket_count-- > 0) if (*s++ != '>')
|
221
|
+
# parser.c:744| {
|
222
|
+
# parser.c:745| *errorptr = s[-1] == 0
|
223
|
+
# parser.c:746| ? US"'>' missing at end of address"
|
224
|
+
# parser.c:747| : string_sprintf("malformed address: %.32s may not follow %.*s",
|
225
|
+
# parser.c:748| s-1, (int)(s - US mailbox - 1), mailbox);
|
226
|
+
# parser.c:749| goto PARSE_FAILED;
|
227
|
+
# parser.c:750| }
|
228
|
+
cv = Sisimai::Address.s3s4(e[p1 + 1, p2 - p1 - 1])
|
229
|
+
v['diagnosis'] = Sisimai::String.sweep(e[p2 + 1, e.size])
|
230
|
+
else
|
231
|
+
# There is an email address only in the line
|
232
|
+
# kijitora@example.jp
|
233
|
+
cv = Sisimai::Address.s3s4(e[2, e.size])
|
234
|
+
end
|
218
235
|
end
|
219
|
-
v['recipient'] =
|
236
|
+
v['recipient'] = cv
|
220
237
|
recipients += 1
|
221
238
|
|
222
|
-
elsif
|
223
|
-
e.match(/\A[ ]+generated[ ]by[ ]([^ \t]+[@][^ \t]+)/)
|
239
|
+
elsif e.include?(' (generated from ') || e.include?(' generated by ')
|
224
240
|
# (generated from kijitora@example.jp)
|
225
241
|
# pipe to |/bin/echo "Some pipe output"
|
226
242
|
# generated by userx@myhost.test.ex
|
227
|
-
v['alias'] =
|
243
|
+
v['alias'] = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
|
244
|
+
|
228
245
|
else
|
229
246
|
next if e.empty?
|
230
247
|
|
231
|
-
if
|
248
|
+
if StartingOf[:frozen].any? { |a| e.include?(a) }
|
232
249
|
# Message *** has been frozen by the system filter.
|
233
250
|
# Message *** was frozen on arrival by ACL.
|
234
251
|
v['alterrors'] ||= ''
|
235
252
|
v['alterrors'] << e + ' '
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
else
|
257
|
-
# Other DSN fields defined in RFC3464
|
258
|
-
next unless fieldtable[o[0]]
|
259
|
-
v[fieldtable[o[0]]] = o[2]
|
260
|
-
end
|
253
|
+
elsif !boundary00.empty?
|
254
|
+
# --NNNNNNNNNN-eximdsn-MMMMMMMMMM
|
255
|
+
# Content-type: message/delivery-status
|
256
|
+
# ...
|
257
|
+
if Sisimai::RFC1894.match(e)
|
258
|
+
# "e" matched with any field defined in RFC3464
|
259
|
+
next unless o = Sisimai::RFC1894.field(e)
|
260
|
+
|
261
|
+
if o[-1] == 'addr'
|
262
|
+
# Final-Recipient: rfc822; kijitora@example.jp
|
263
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
264
|
+
next unless o[0] == 'final-recipient'
|
265
|
+
v['spec'] ||= o[2].include?('@') ? 'SMTP' : 'X-UNIX'
|
266
|
+
|
267
|
+
elsif o[-1] == 'code'
|
268
|
+
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
269
|
+
v['spec'] = o[1]
|
270
|
+
v['diagnosis'] = o[2]
|
271
|
+
|
261
272
|
else
|
262
|
-
#
|
263
|
-
next
|
273
|
+
# Other DSN fields defined in RFC3464
|
274
|
+
next unless fieldtable[o[0]]
|
275
|
+
v[fieldtable[o[0]]] = o[2]
|
276
|
+
end
|
277
|
+
else
|
278
|
+
# Error message ?
|
279
|
+
next if nextcursor == 1
|
264
280
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
end
|
281
|
+
# Content-type: message/delivery-status
|
282
|
+
nextcursor = 1 if e.start_with?(StartingOf[:deliverystatus][0])
|
283
|
+
v['alterrors'] ||= ''
|
284
|
+
if e.start_with?("\s", "\t")
|
285
|
+
e.sub!(/\A[\s\t]+/, '')
|
286
|
+
v['alterrors'] << e + ' ' unless v['alterrors'].include?(e)
|
272
287
|
end
|
288
|
+
end
|
289
|
+
else
|
290
|
+
# There is no boundary string in $boundary00
|
291
|
+
if dscontents.size == recipients
|
292
|
+
# Error message
|
293
|
+
next if e.empty?
|
294
|
+
v['diagnosis'] ||= ''
|
295
|
+
v['diagnosis'] << e + ' '
|
273
296
|
else
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
v['diagnosis']
|
297
|
+
# Error message when email address above does not include '@' and domain part.
|
298
|
+
if e.include?(' pipe to |/')
|
299
|
+
# pipe to |/path/to/prog ...
|
300
|
+
# generated by kijitora@example.com
|
301
|
+
v['diagnosis'] = e
|
279
302
|
else
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
# pipe to |/path/to/prog ...
|
284
|
-
# generated by kijitora@example.com
|
285
|
-
v['diagnosis'] = e
|
286
|
-
else
|
287
|
-
next unless e.start_with?(' ')
|
288
|
-
v['alterrors'] ||= ''
|
289
|
-
v['alterrors'] << e + ' '
|
290
|
-
end
|
303
|
+
next unless e.start_with?(' ')
|
304
|
+
v['alterrors'] ||= ''
|
305
|
+
v['alterrors'] << e + ' '
|
291
306
|
end
|
292
307
|
end
|
293
308
|
end
|
@@ -330,7 +345,9 @@ module Sisimai::Lhost
|
|
330
345
|
unless mhead['received'].empty?
|
331
346
|
# Get the name of local MTA
|
332
347
|
# Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
|
333
|
-
|
348
|
+
p1 = mhead['received'][-1].index('from ') || -1
|
349
|
+
p2 = mhead['received'][-1].index(' ', p1 + 5) || -1
|
350
|
+
localhost0 = mhead['received'][-1][p1 + 5, p2 - p1 - 5] if p1 > -1 && p2 > -1
|
334
351
|
end
|
335
352
|
|
336
353
|
dscontents.each do |e|
|
@@ -371,25 +388,23 @@ module Sisimai::Lhost
|
|
371
388
|
if e['diagnosis'].start_with?('-') || e['diagnosis'].end_with?('__')
|
372
389
|
# Override the value of diagnostic code message
|
373
390
|
e['diagnosis'] = e['alterrors'] unless e['alterrors'].empty?
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
# Override the value of diagnostic code message because
|
380
|
-
# the value of alterrors includes the value of diagnosis.
|
381
|
-
e['diagnosis'] = e['alterrors'] if e['alterrors'].downcase.include?(e['diagnosis'].downcase)
|
382
|
-
end
|
391
|
+
|
392
|
+
elsif e['diagnosis'].size < e['alterrors'].size
|
393
|
+
# Override the value of diagnostic code message because the value of alterrors includes
|
394
|
+
# the value of diagnosis.
|
395
|
+
e['diagnosis'] = e['alterrors'] if e['alterrors'].downcase.include?(e['diagnosis'].downcase)
|
383
396
|
end
|
384
397
|
e.delete('alterrors')
|
385
398
|
end
|
386
|
-
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
|
387
|
-
e['diagnosis']
|
399
|
+
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''; p1 = e['diagnosis'].index('__') || -1
|
400
|
+
e['diagnosis'] = e['diagnosis'][0, p1] if p1 > 1
|
388
401
|
|
389
402
|
unless e['rhost']
|
390
403
|
# Get the remote host name
|
391
404
|
# host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
|
392
|
-
|
405
|
+
p1 = e['diagnosis'].index('host ') || -1
|
406
|
+
p2 = e['diagnosis'].index(' ', p1 + 5) || -1
|
407
|
+
e['rhost'] = e['diagnosis'][p1 + 5, p2 - p1 - 5] if p1 > -1
|
393
408
|
|
394
409
|
unless e['rhost']
|
395
410
|
# Get localhost and remote host name from Received header.
|
@@ -426,55 +441,54 @@ module Sisimai::Lhost
|
|
426
441
|
|
427
442
|
unless e['reason']
|
428
443
|
# The reason "expired"
|
429
|
-
e['reason']
|
444
|
+
e['reason'] = 'expired' if DelayedFor.any? { |a| e['diagnosis'].include?(a) }
|
445
|
+
e['reason'] ||= 'mailererror' if e['diagnosis'].include?('pipe to |')
|
430
446
|
end
|
431
447
|
end
|
432
448
|
end
|
433
449
|
|
434
450
|
# Prefer the value of smtp reply code in Diagnostic-Code:
|
435
|
-
# See eg/maildir-as-a-sample/new/exim-20.eml
|
436
451
|
# Action: failed
|
437
452
|
# Final-Recipient: rfc822;userx@test.ex
|
438
453
|
# Status: 5.0.0
|
439
454
|
# Remote-MTA: dns; 127.0.0.1
|
440
455
|
# Diagnostic-Code: smtp; 450 TEMPERROR: retry timeout exceeded
|
441
|
-
#
|
442
|
-
# of SMTP reply code in
|
443
|
-
|
444
|
-
|
456
|
+
#
|
457
|
+
# The value of "Status:" indicates permanent error but the value of SMTP reply code in
|
458
|
+
# Diagnostic-Code: field is "TEMPERROR"!!!!
|
459
|
+
cs = e['status'] || Sisimai::SMTP::Status.find(e['diagnosis'])
|
460
|
+
cr = e['replycode'] || Sisimai::SMTP::Reply.find(e['diagnosis'])
|
445
461
|
s1 = 0 # First character of Status as integer
|
446
462
|
r1 = 0 # First character of SMTP reply code as integer
|
447
463
|
|
448
464
|
while true
|
449
465
|
# "Status:" field did not exist in the bounce message
|
450
|
-
break if
|
451
|
-
break unless
|
466
|
+
break if cs
|
467
|
+
break unless cr
|
452
468
|
|
453
|
-
# Check SMTP reply code
|
454
|
-
|
455
|
-
r1 = rv[0, 1].to_i
|
469
|
+
# Check SMTP reply code, Generate pseudo DSN code from SMTP reply code
|
470
|
+
r1 = cr[0, 1].to_i
|
456
471
|
if r1 == 4
|
457
472
|
# Get the internal DSN(temporary error)
|
458
|
-
|
473
|
+
cs = Sisimai::SMTP::Status.code(e['reason'], true)
|
459
474
|
|
460
475
|
elsif r1 == 5
|
461
476
|
# Get the internal DSN(permanent error)
|
462
|
-
|
477
|
+
cs = Sisimai::SMTP::Status.code(e['reason'], false)
|
463
478
|
end
|
464
479
|
break
|
465
480
|
end
|
466
481
|
|
467
|
-
s1 =
|
482
|
+
s1 = cs[0, 1].to_i if cs
|
468
483
|
v1 = s1 + r1
|
469
484
|
v1 << e['status'][0, 1].to_i if e['status']
|
470
485
|
|
471
486
|
if v1 > 0
|
472
|
-
# Status or SMTP reply code exists
|
473
|
-
|
474
|
-
e['status'] = sv if r1 > 0
|
487
|
+
# Status or SMTP reply code exists, Set pseudo DSN into the value of "status" accessor
|
488
|
+
e['status'] = cs if r1 > 0
|
475
489
|
else
|
476
490
|
# Neither Status nor SMTP reply code exist
|
477
|
-
|
491
|
+
cs = if %w[expired mailboxfull].include?(e['reason'])
|
478
492
|
# Set pseudo DSN (temporary error)
|
479
493
|
Sisimai::SMTP::Status.code(e['reason'], true)
|
480
494
|
else
|
@@ -482,10 +496,10 @@ module Sisimai::Lhost
|
|
482
496
|
Sisimai::SMTP::Status.code(e['reason'], false)
|
483
497
|
end
|
484
498
|
end
|
485
|
-
e['status'] ||=
|
499
|
+
e['status'] ||= cs.to_s
|
486
500
|
end
|
487
501
|
|
488
|
-
return { 'ds' => dscontents, 'rfc822' =>
|
502
|
+
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
489
503
|
end
|
490
504
|
def description; return 'Exim'; end
|
491
505
|
end
|