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/rfc3464.rb
CHANGED
@@ -1,108 +1,112 @@
|
|
1
1
|
module Sisimai
|
2
2
|
# Sisimai::RFC3464 - bounce mail parser class for Fallback.
|
3
3
|
module RFC3464
|
4
|
-
# Imported from p5-Sisimail/lib/Sisimai/RFC3464.pm
|
5
4
|
class << self
|
6
5
|
require 'sisimai/lhost'
|
6
|
+
require 'sisimai/address'
|
7
|
+
require 'sisimai/rfc1894'
|
7
8
|
|
8
9
|
# http://tools.ietf.org/html/rfc3464
|
9
10
|
Indicators = Sisimai::Lhost.INDICATORS
|
10
|
-
|
11
|
-
message:
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
content-type:[ ]*(?:message/rfc822|text/rfc822-headers)
|
27
|
-
|return-path:[ ]*[<].+[>]
|
28
|
-
)\z
|
29
|
-
}x,
|
30
|
-
error: %r/\A(?:[45]\d\d[ \t]+|[<][^@]+[@][^@]+[>]:?[ \t]+)/,
|
31
|
-
command: %r/[ ](RCPT|MAIL|DATA)[ ]+command\b/,
|
11
|
+
StartingOf = {
|
12
|
+
message: [
|
13
|
+
'content-type: message/delivery-status',
|
14
|
+
'content-type: message/disposition-notification',
|
15
|
+
'content-type: text/plain; charset=',
|
16
|
+
'the original message was received at ',
|
17
|
+
'this report relates to your message',
|
18
|
+
'your message could not be delivered',
|
19
|
+
'your message was not delivered to ',
|
20
|
+
'your message was not delivered to the following recipients',
|
21
|
+
],
|
22
|
+
rfc822: [
|
23
|
+
'content-type: message/rfc822',
|
24
|
+
'content-type: text/rfc822-headers',
|
25
|
+
'return-path: <'
|
26
|
+
],
|
32
27
|
}.freeze
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
28
|
+
ReadUntil0 = [
|
29
|
+
# Stop reading when the following string have appeared at the first of a line
|
30
|
+
'a copy of the original message below this line:',
|
31
|
+
'content-type: message/delivery-status',
|
32
|
+
'for further assistance, please contact ',
|
33
|
+
'here is a copy of the first part of the message',
|
34
|
+
'received:',
|
35
|
+
'received-from-mta:',
|
36
|
+
'reporting-mta:',
|
37
|
+
'reporting-ua:',
|
38
|
+
'return-path:',
|
39
|
+
'the non-delivered message is attached to this message',
|
40
|
+
].freeze
|
41
|
+
ReadUntil1 = [
|
42
|
+
# Stop reading when the following string have appeared in a line
|
43
|
+
'attachment is a copy of the message',
|
44
|
+
'below is a copy of the original message:',
|
45
|
+
'below this line is a copy of the message',
|
46
|
+
'message contains ',
|
47
|
+
'message text follows: ',
|
48
|
+
'original message follows',
|
49
|
+
'the attachment contains the original mail headers',
|
50
|
+
'the first ',
|
51
|
+
'unsent message below',
|
52
|
+
'your message reads (in part):',
|
53
|
+
].freeze
|
54
|
+
ReadAfter0 = [
|
55
|
+
# Do not read before the following strings
|
56
|
+
' the postfix ',
|
57
|
+
'a summary of the undelivered message you sent follows:',
|
58
|
+
'the following is the error message',
|
59
|
+
'the message that you sent was undeliverable to the following',
|
60
|
+
'your message was not delivered to ',
|
61
|
+
].freeze
|
62
|
+
DoNotRead0 = [' -----', ' -----', '--', '|--', '*'].freeze
|
63
|
+
DoNotRead1 = ['mail from:', 'message-id:', ' from: '].freeze
|
64
|
+
ReadEmail0 = [' ', '"', '<',].freeze
|
65
|
+
ReadEmail1 = [
|
66
|
+
# There is an email address around the following strings
|
67
|
+
'address:',
|
68
|
+
'addressed to',
|
69
|
+
'could not be delivered to:',
|
70
|
+
'delivered to',
|
71
|
+
'delivery failed:',
|
72
|
+
'did not reach the following recipient:',
|
73
|
+
'error-for:',
|
74
|
+
'failed recipient:',
|
75
|
+
'failed to deliver to',
|
76
|
+
'intended recipient:',
|
77
|
+
'mailbox is full:',
|
78
|
+
'recipient:',
|
79
|
+
'rcpt to:',
|
80
|
+
'smtp server <',
|
81
|
+
'the following recipients returned permanent errors:',
|
82
|
+
'the following addresses had permanent errors',
|
83
|
+
'the following message to',
|
84
|
+
'to: ',
|
85
|
+
'unknown user:',
|
86
|
+
'unable to deliver mail to the following recipient',
|
87
|
+
'undeliverable to',
|
88
|
+
'undeliverable address:',
|
89
|
+
'you sent mail to',
|
90
|
+
'your message has encountered delivery problems to the following recipients:',
|
91
|
+
'was automatically rejected',
|
92
|
+
'was rejected due to',
|
93
|
+
].freeze
|
94
94
|
|
95
95
|
# Detect an error for RFC3464
|
96
96
|
# @param [Hash] mhead Message headers of a bounce email
|
97
97
|
# @param [String] mbody Message body of a bounce email
|
98
98
|
# @return [Hash] Bounce data list and message/rfc822 part
|
99
99
|
# @return [Nil] it failed to parse or the arguments are missing
|
100
|
-
def
|
100
|
+
def inquire(mhead, mbody)
|
101
|
+
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
102
|
+
permessage = {} # (Hash) Store values of each Per-Message field
|
103
|
+
|
101
104
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
102
105
|
bodyslices = mbody.scrub('?').split("\n")
|
103
106
|
readslices = ['']
|
104
107
|
rfc822text = '' # (String) message/rfc822 part text
|
105
108
|
maybealias = nil # (String) Original-Recipient Field
|
109
|
+
lowercased = '' # (String) Lowercased each line of the loop
|
106
110
|
blanklines = 0 # (Integer) The number of blank lines
|
107
111
|
readcursor = 0 # (Integer) Points the current cursor position
|
108
112
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
@@ -115,14 +119,14 @@ module Sisimai
|
|
115
119
|
v = nil
|
116
120
|
|
117
121
|
while e = bodyslices.shift do
|
118
|
-
# Read error messages and delivery status lines from the head of the email
|
119
|
-
#
|
122
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
123
|
+
# line of the beginning of the original message.
|
120
124
|
readslices << e # Save the current line for the next loop
|
121
|
-
|
125
|
+
lowercased = e.downcase
|
122
126
|
|
123
127
|
if readcursor == 0
|
124
128
|
# Beginning of the bounce message or delivery status part
|
125
|
-
if
|
129
|
+
if StartingOf[:message].any? { |a| lowercased.start_with?(a) }
|
126
130
|
readcursor |= Indicators[:deliverystatus]
|
127
131
|
next
|
128
132
|
end
|
@@ -130,7 +134,7 @@ module Sisimai
|
|
130
134
|
|
131
135
|
if (readcursor & Indicators[:'message-rfc822']) == 0
|
132
136
|
# Beginning of the original message part
|
133
|
-
if
|
137
|
+
if StartingOf[:rfc822].any? { |a| lowercased == a }
|
134
138
|
readcursor |= Indicators[:'message-rfc822']
|
135
139
|
next
|
136
140
|
end
|
@@ -150,189 +154,75 @@ module Sisimai
|
|
150
154
|
next if e.empty?
|
151
155
|
|
152
156
|
v = dscontents[-1]
|
153
|
-
if
|
154
|
-
#
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
157
|
+
if f = Sisimai::RFC1894.match(e)
|
158
|
+
# "e" matched with any field defined in RFC3464
|
159
|
+
next unless o = Sisimai::RFC1894.field(e)
|
160
|
+
|
161
|
+
if o[-1] == 'addr'
|
162
|
+
# Final-Recipient: rfc822; kijitora@example.jp
|
163
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
164
|
+
if o[0] == 'final-recipient' || o[0] == 'original-recipient'
|
165
|
+
# Final-Recipient: rfc822; kijitora@example.jp
|
166
|
+
if o[0] == 'original-recipient'
|
167
|
+
# Original-Recipient: ...
|
168
|
+
maybealias = o[2]
|
169
|
+
else
|
170
|
+
# Final-Recipient: ...
|
171
|
+
x = v['recipient'] || ''
|
172
|
+
y = Sisimai::Address.s3s4(o[2])
|
173
|
+
y = maybealias unless Sisimai::Address.is_emailaddress(y)
|
174
|
+
|
175
|
+
if !x.empty? && x != y
|
176
|
+
# There are multiple recipient addresses in the message body.
|
177
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
178
|
+
v = dscontents[-1]
|
179
|
+
end
|
180
|
+
v['recipient'] = y
|
181
|
+
recipients += 1
|
182
|
+
itisbounce ||= true
|
183
|
+
|
184
|
+
v['alias'] ||= maybealias
|
185
|
+
maybealias = nil
|
186
|
+
end
|
187
|
+
elsif o[0] == 'x-actual-recipient'
|
188
|
+
# X-Actual-Recipient: RFC822; |IFS=' ' && exec procmail -f- || exit 75 ...
|
189
|
+
# X-Actual-Recipient: rfc822; kijitora@neko.example.jp
|
190
|
+
v['alias'] = o[2] unless o[2].include?(' ')
|
185
191
|
end
|
186
|
-
|
187
|
-
|
188
|
-
|
192
|
+
elsif o[-1] == 'code'
|
193
|
+
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
194
|
+
v['spec'] = o[1]
|
195
|
+
v['diagnosis'] = o[2]
|
196
|
+
else
|
197
|
+
# Other DSN fields defined in RFC3464
|
198
|
+
next unless fieldtable[o[0]]
|
199
|
+
v[fieldtable[o[0]]] = o[2]
|
189
200
|
|
190
|
-
|
191
|
-
|
201
|
+
next unless f
|
202
|
+
permessage[fieldtable[o[0]]] = o[2]
|
192
203
|
end
|
193
|
-
|
194
|
-
elsif cv = e.match(/\AX-Actual-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
|
195
|
-
# X-Actual-Recipient: RFC822; |IFS=' ' && exec procmail -f- || exit 75 ...
|
196
|
-
# X-Actual-Recipient: rfc822; kijitora@neko.example.jp
|
197
|
-
v['alias'] = cv[1] unless cv[1] =~ /[ \t]+/
|
198
|
-
|
199
|
-
elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
|
200
|
-
# 2.3.3 Action field
|
201
|
-
# The Action field indicates the action performed by the Reporting-MTA
|
202
|
-
# as a result of its attempt to deliver the message to this recipient
|
203
|
-
# address. This field MUST be present for each recipient named in the
|
204
|
-
# DSN.
|
205
|
-
# The syntax for the action-field is:
|
206
|
-
#
|
207
|
-
# action-field = "Action" ":" action-value
|
208
|
-
# action-value =
|
209
|
-
# "failed" / "delayed" / "delivered" / "relayed" / "expanded"
|
210
|
-
#
|
211
|
-
# The action-value may be spelled in any combination of upper and lower
|
212
|
-
# case characters.
|
213
|
-
v['action'] = cv[1].downcase
|
214
|
-
|
215
|
-
# failed (bad destination mailbox address)
|
216
|
-
if cv = v['action'].match(/\A([^ ]+)[ ]/) then v['action'] = cv[1] end
|
217
|
-
|
218
|
-
elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
|
219
|
-
# 2.3.4 Status field
|
220
|
-
# The per-recipient Status field contains a transport-independent
|
221
|
-
# status code that indicates the delivery status of the message to that
|
222
|
-
# recipient. This field MUST be present for each delivery attempt
|
223
|
-
# which is described by a DSN.
|
224
|
-
#
|
225
|
-
# The syntax of the status field is:
|
226
|
-
#
|
227
|
-
# status-field = "Status" ":" status-code
|
228
|
-
# status-code = DIGIT "." 1*3DIGIT "." 1*3DIGIT
|
229
|
-
v['status'] = cv[1]
|
230
|
-
|
231
|
-
elsif cv = e.match(/\AStatus:[ ]*(\d+[ ]+.+)\z/)
|
232
|
-
# Status: 553 Exceeded maximum inbound message size
|
233
|
-
v['alterrors'] = cv[1]
|
234
|
-
|
235
|
-
elsif cv = e.match(/\ARemote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
|
236
|
-
# 2.3.5 Remote-MTA field
|
237
|
-
# The value associated with the Remote-MTA DSN field is a printable
|
238
|
-
# ASCII representation of the name of the "remote" MTA that reported
|
239
|
-
# delivery status to the "reporting" MTA.
|
240
|
-
#
|
241
|
-
# remote-mta-field = "Remote-MTA" ":" mta-name-type ";" mta-name
|
242
|
-
#
|
243
|
-
# NOTE: The Remote-MTA field preserves the "while talking to"
|
244
|
-
# information that was provided in some pre-existing nondelivery
|
245
|
-
# reports.
|
246
|
-
#
|
247
|
-
# This field is optional. It MUST NOT be included if no remote MTA was
|
248
|
-
# involved in the attempted delivery of the message to that recipient.
|
249
|
-
v['rhost'] = cv[1].downcase
|
250
|
-
|
251
|
-
elsif cv = e.match(/\ALast-Attempt-Date:[ ]*(.+)\z/)
|
252
|
-
# 2.3.7 Last-Attempt-Date field
|
253
|
-
# The Last-Attempt-Date field gives the date and time of the last
|
254
|
-
# attempt to relay, gateway, or deliver the message (whether successful
|
255
|
-
# or unsuccessful) by the Reporting MTA. This is not necessarily the
|
256
|
-
# same as the value of the Date field from the header of the message
|
257
|
-
# used to transmit this delivery status notification: In cases where
|
258
|
-
# the DSN was generated by a gateway, the Date field in the message
|
259
|
-
# header contains the time the DSN was sent by the gateway and the DSN
|
260
|
-
# Last-Attempt-Date field contains the time the last delivery attempt
|
261
|
-
# occurred.
|
262
|
-
#
|
263
|
-
# last-attempt-date-field = "Last-Attempt-Date" ":" date-time
|
264
|
-
v['date'] = cv[1]
|
265
204
|
else
|
266
|
-
|
267
|
-
|
268
|
-
#
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
#
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
v['spec'] = cv[1].upcase
|
277
|
-
v['diagnosis'] = cv[2]
|
278
|
-
|
279
|
-
elsif cv = e.match(/\ADiagnostic-Code:[ ]*(.+)\z/)
|
280
|
-
# No value of "diagnostic-type"
|
281
|
-
# Diagnostic-Code: 554 ...
|
282
|
-
v['diagnosis'] = cv[1]
|
283
|
-
|
284
|
-
elsif readslices[-2].start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
|
205
|
+
# The line did not match with any fields defined in RFC3464
|
206
|
+
if e.start_with?('Diagnostic-Code: ') && e.include?(';') == false
|
207
|
+
# There is no value of "diagnostic-type" such as Diagnostic-Code: 554 ...
|
208
|
+
v['diagnosis'] = e[e.index(' ') + 1, e.size]
|
209
|
+
|
210
|
+
elsif e.start_with?('Status: ') && Sisimai::SMTP::Reply.find(e[8, 3])
|
211
|
+
# Status: 553 Exceeded maximum inbound message size
|
212
|
+
v['alterrors'] = e[8, e.size]
|
213
|
+
|
214
|
+
elsif readslices[-2].start_with?('Diagnostic-Code:') && cv = e.start_with?(' ')
|
285
215
|
# Continued line of the value of Diagnostic-Code header
|
286
|
-
v['diagnosis'] << ' ' <<
|
216
|
+
v['diagnosis'] << ' ' << e
|
287
217
|
readslices[-1] = 'Diagnostic-Code: ' << e
|
288
218
|
else
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
# The Reporting-MTA field is defined as follows:
|
297
|
-
#
|
298
|
-
# A DSN describes the results of attempts to deliver, relay, or gateway
|
299
|
-
# a message to one or more recipients. In all cases, the Reporting-MTA
|
300
|
-
# is the MTA that attempted to perform the delivery, relay, or gateway
|
301
|
-
# operation described in the DSN. This field is required.
|
302
|
-
connheader['rhost'] ||= cv[1].downcase
|
303
|
-
|
304
|
-
elsif cv = e.match(/\AReceived-From-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
|
305
|
-
# 2.2.4 The Received-From-MTA DSN field
|
306
|
-
# The optional Received-From-MTA field indicates the name of the MTA
|
307
|
-
# from which the message was received.
|
308
|
-
#
|
309
|
-
# received-from-mta-field =
|
310
|
-
# "Received-From-MTA" ":" mta-name-type ";" mta-name
|
311
|
-
#
|
312
|
-
# If the message was received from an Internet host via SMTP, the
|
313
|
-
# contents of the mta-name sub-field SHOULD be the Internet domain name
|
314
|
-
# supplied in the HELO or EHLO command, and the network address used by
|
315
|
-
# the SMTP client SHOULD be included as a comment enclosed in
|
316
|
-
# parentheses. (In this case, the MTA-name-type will be "dns".)
|
317
|
-
connheader['lhost'] = cv[1].downcase
|
318
|
-
|
319
|
-
elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
|
320
|
-
# 2.2.5 The Arrival-Date DSN field
|
321
|
-
# The optional Arrival-Date field indicates the date and time at which
|
322
|
-
# the message arrived at the Reporting MTA. If the Last-Attempt-Date
|
323
|
-
# field is also provided in a per-recipient field, this can be used to
|
324
|
-
# determine the interval between when the message arrived at the
|
325
|
-
# Reporting MTA and when the report was issued for that recipient.
|
326
|
-
#
|
327
|
-
# arrival-date-field = "Arrival-Date" ":" date-time
|
328
|
-
connheader['date'] = cv[1]
|
329
|
-
else
|
330
|
-
# Get error message
|
331
|
-
next if e.start_with?(' ', '-')
|
332
|
-
next unless e =~ MarkingsOf[:error]
|
333
|
-
|
334
|
-
# 500 User Unknown
|
335
|
-
# <kijitora@example.jp> Unknown
|
219
|
+
# Get error messages which is written in the message body directly
|
220
|
+
next if e.start_with?(' ', ' ', 'X')
|
221
|
+
cr = Sisimai::SMTP::Reply.find(e) || ''
|
222
|
+
ca = Sisimai::Address.find(e) || []
|
223
|
+
co = Sisimai::String.aligned(e, ['<', '@', '>'])
|
224
|
+
|
225
|
+
if cr.size > 0 || (ca.size > 0 && co)
|
336
226
|
v['alterrors'] ||= ' '
|
337
227
|
v['alterrors'] << ' ' << e
|
338
228
|
end
|
@@ -341,50 +231,101 @@ module Sisimai
|
|
341
231
|
end # End of if: rfc822
|
342
232
|
end
|
343
233
|
|
234
|
+
# -----------------------------------------------------------------------------------------
|
344
235
|
while true
|
345
236
|
# Fallback, parse entire message body
|
346
237
|
break if recipients > 0
|
347
|
-
match = 0
|
348
|
-
mfrom = mhead['from'].downcase
|
349
238
|
|
350
239
|
# Failed to get a recipient address at code above
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
end
|
368
|
-
break unless match > 0
|
240
|
+
returnpath = (mhead['return-path'] || '').downcase
|
241
|
+
headerfrom = (mhead['from'] || '').downcase
|
242
|
+
errortitle = (mhead['subject'] || '').downcase
|
243
|
+
patternsof = {
|
244
|
+
'from' => ['postmaster@', 'mailer-daemon@', 'root@'],
|
245
|
+
'return-path' => ['<>', 'mailer-daemon'],
|
246
|
+
'subject' => ['delivery fail', 'delivery report', 'failure notice', 'mail delivery',
|
247
|
+
'mail failed', 'mail error', 'non-delivery', 'returned mail',
|
248
|
+
'undeliverable mail', 'warning: '],
|
249
|
+
}
|
250
|
+
|
251
|
+
match = nil
|
252
|
+
match ||= patternsof['from'].any? { |v| headerfrom.include?(v) }
|
253
|
+
match ||= patternsof['subject'].any? { |v| errortitle.include?(v) }
|
254
|
+
match ||= patternsof['return-path'].any? { |v| returnpath.include?(v) }
|
255
|
+
break unless match
|
369
256
|
|
370
257
|
b = dscontents[-1]
|
258
|
+
hasmatched = 0 # There may be an email address around the line
|
259
|
+
readslices = [] # Previous line of this loop
|
260
|
+
|
261
|
+
ReadAfter0.each do |e|
|
262
|
+
# Cut strings from the begining of "mbody" to the strings defined in ReadAfter0
|
263
|
+
i = mbody.downcase.index(e)
|
264
|
+
next unless i
|
265
|
+
mbody = mbody[i, mbody.size - i]
|
266
|
+
end
|
267
|
+
lowercased = mbody.downcase
|
371
268
|
bodyslices = mbody.split("\n")
|
269
|
+
|
372
270
|
while e = bodyslices.shift do
|
373
271
|
# Get the recipient's email address and error messages.
|
374
|
-
d = e.downcase
|
375
|
-
break if d =~ MarkingsOf[:rfc822]
|
376
|
-
break if d =~ ReStop
|
377
|
-
|
378
272
|
next if e.empty?
|
379
|
-
|
380
|
-
|
273
|
+
hasmatched = 0
|
274
|
+
lowercased = e.downcase
|
275
|
+
readslices << lowercased
|
276
|
+
|
277
|
+
break if StartingOf[:rfc822].include?(lowercased)
|
278
|
+
break if ReadUntil0.any? { |v| lowercased.start_with?(v) }
|
279
|
+
break if ReadUntil1.any? { |v| lowercased.include?(v) }
|
280
|
+
next if DoNotRead0.any? { |v| lowercased.start_with?(v) }
|
281
|
+
next if DoNotRead1.any? { |v| lowercased.include?(v) }
|
282
|
+
|
283
|
+
while true do
|
284
|
+
# There is an email address with an error message at this line(1)
|
285
|
+
break unless ReadEmail0.any? { |v| lowercased.start_with?(v) }
|
286
|
+
break unless lowercased.include?('@')
|
287
|
+
|
288
|
+
hasmatched = 1
|
289
|
+
break
|
290
|
+
end
|
291
|
+
|
292
|
+
while true do
|
293
|
+
# There is an email address with an error message at this line(2)
|
294
|
+
break if hasmatched > 0
|
295
|
+
break unless ReadEmail1.any? { |v| lowercased.include?(v) }
|
296
|
+
break unless lowercased.include?('@')
|
297
|
+
|
298
|
+
hasmatched = 2
|
299
|
+
break
|
300
|
+
end
|
381
301
|
|
382
|
-
|
302
|
+
while true do
|
303
|
+
# There is an email address without an error message at this line
|
304
|
+
break if hasmatched > 0
|
305
|
+
break if readslices.size < 2
|
306
|
+
break unless ReadEmail1.any? { |v| readslices[-2].include?(v) }
|
307
|
+
break unless lowercased.include?('@') # Must contain '@'
|
308
|
+
break unless lowercased.include?('.') # Must contain '.'
|
309
|
+
break if lowercased.include?('$')
|
310
|
+
|
311
|
+
hasmatched = 3
|
312
|
+
break
|
313
|
+
end
|
314
|
+
|
315
|
+
if hasmatched > 0 && lowercased.include?('@')
|
383
316
|
# May be an email address
|
317
|
+
w = e.split(' ')
|
384
318
|
x = b['recipient'] || ''
|
385
|
-
y =
|
386
|
-
|
387
|
-
|
319
|
+
y = ''
|
320
|
+
|
321
|
+
w.each do |ee|
|
322
|
+
# Find an email address (including "@")
|
323
|
+
next unless ee.include?('@')
|
324
|
+
y = Sisimai::Address.s3s4(ee)
|
325
|
+
next unless Sisimai::Address.is_emailaddress(y)
|
326
|
+
break
|
327
|
+
end
|
328
|
+
|
388
329
|
if !x.empty? && x != y
|
389
330
|
# There are multiple recipient addresses in the message body.
|
390
331
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
@@ -394,9 +335,9 @@ module Sisimai
|
|
394
335
|
recipients += 1
|
395
336
|
itisbounce ||= true
|
396
337
|
|
397
|
-
elsif
|
338
|
+
elsif e.include?('(expanded from') || e.include?('(generated from')
|
398
339
|
# (expanded from: neko@example.jp)
|
399
|
-
b['alias'] = Sisimai::Address.s3s4(
|
340
|
+
b['alias'] = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
|
400
341
|
end
|
401
342
|
b['diagnosis'] ||= ''
|
402
343
|
b['diagnosis'] << ' ' << e
|
@@ -406,9 +347,11 @@ module Sisimai
|
|
406
347
|
end
|
407
348
|
return nil unless itisbounce
|
408
349
|
|
409
|
-
|
350
|
+
p1 = rfc822text.index("\nTo: ") || -1
|
351
|
+
p2 = rfc822text.index("\n", p1 + 6) || -1
|
352
|
+
if recipients == 0 && p1 > 0
|
410
353
|
# Try to get a recipient address from "To:" header of the original message
|
411
|
-
if r = Sisimai::Address.find(
|
354
|
+
if r = Sisimai::Address.find(rfc822text[p1 + 5, p2 - p1 - 5], true)
|
412
355
|
# Found a recipient address
|
413
356
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS if dscontents.size == recipients
|
414
357
|
b = dscontents[-1]
|
@@ -418,8 +361,9 @@ module Sisimai
|
|
418
361
|
end
|
419
362
|
return nil unless recipients > 0
|
420
363
|
|
364
|
+
require 'sisimai/smtp/command'
|
421
365
|
require 'sisimai/mda'
|
422
|
-
mdabounced = Sisimai::MDA.
|
366
|
+
mdabounced = Sisimai::MDA.inquire(mhead, mbody)
|
423
367
|
dscontents.each do |e|
|
424
368
|
# Set default values if each value is empty.
|
425
369
|
connheader.each_key { |a| e[a] ||= connheader[a] || '' }
|
@@ -438,18 +382,16 @@ module Sisimai
|
|
438
382
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
|
439
383
|
|
440
384
|
if mdabounced
|
441
|
-
# Make bounce data by the values returned from Sisimai::MDA.
|
385
|
+
# Make bounce data by the values returned from Sisimai::MDA.inquire()
|
442
386
|
e['agent'] = mdabounced['mda'] || 'RFC3464'
|
443
387
|
e['reason'] = mdabounced['reason'] || 'undefined'
|
444
388
|
e['diagnosis'] = mdabounced['message'] unless mdabounced['message'].empty?
|
445
|
-
e['command'] =
|
389
|
+
e['command'] = nil
|
446
390
|
end
|
447
391
|
|
448
|
-
e['
|
449
|
-
|
450
|
-
|
451
|
-
end
|
452
|
-
e['date'] ||= mhead['date']
|
392
|
+
e['date'] ||= mhead['date']
|
393
|
+
e['status'] ||= Sisimai::SMTP::Status.find(e['diagnosis']) || ''
|
394
|
+
e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis'])
|
453
395
|
end
|
454
396
|
|
455
397
|
return { 'ds' => dscontents, 'rfc822' => rfc822text }
|