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
|
@@ -1,34 +1,23 @@
|
|
|
1
1
|
module Sisimai::Lhost
|
|
2
|
-
# Sisimai::Lhost::Postfix parses a bounce email which created by
|
|
3
|
-
#
|
|
2
|
+
# Sisimai::Lhost::Postfix parses a bounce email which created by Postfix. Methods in the module are
|
|
3
|
+
# called from only Sisimai::Message.
|
|
4
4
|
module Postfix
|
|
5
5
|
class << self
|
|
6
|
-
# Imported from p5-Sisimail/lib/Sisimai/Lhost/Postfix.pm
|
|
7
6
|
require 'sisimai/lhost'
|
|
7
|
+
require 'sisimai/smtp/command'
|
|
8
8
|
|
|
9
9
|
# Postfix manual - bounce(5) - http://www.postfix.org/bounce.5.html
|
|
10
10
|
Indicators = Sisimai::Lhost.INDICATORS
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|\w+[ \t]program\z # The <custmized-name> program
|
|
22
|
-
)
|
|
23
|
-
|This[ ]is[ ]the[ ](?:
|
|
24
|
-
Postfix[ ]program # This is the Postfix program
|
|
25
|
-
|\w+[ ]Postfix[ ]program # This is the <name> Postfix program
|
|
26
|
-
|\w+[ ]program # This is the <customized-name> Postfix program
|
|
27
|
-
|mail[ ]system[ ]at[ ]host # This is the mail system at host <hostname>.
|
|
28
|
-
)
|
|
29
|
-
)
|
|
30
|
-
}x,
|
|
31
|
-
# :from => %r/ [(]Mail Delivery System[)]\z/,
|
|
11
|
+
Boundaries = ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'].freeze
|
|
12
|
+
StartingOf = {
|
|
13
|
+
# Postfix manual - bounce(5) - http://www.postfix.org/bounce.5.html
|
|
14
|
+
message: [
|
|
15
|
+
['The ', 'Postfix '], # The Postfix program, The Postfix on <os> program
|
|
16
|
+
['The ', 'mail system'], # The mail system
|
|
17
|
+
['The ', 'program'], # The <name> pogram
|
|
18
|
+
['This is the', 'Postfix'], # This is the Postfix program
|
|
19
|
+
['This is the', 'mail system'], # This is the mail system at host <hostname>
|
|
20
|
+
],
|
|
32
21
|
}.freeze
|
|
33
22
|
|
|
34
23
|
# Parse bounce messages from Postfix
|
|
@@ -36,109 +25,160 @@ module Sisimai::Lhost
|
|
|
36
25
|
# @param [String] mbody Message body of a bounce email
|
|
37
26
|
# @return [Hash] Bounce data list and message/rfc822 part
|
|
38
27
|
# @return [Nil] it failed to parse or the arguments are missing
|
|
39
|
-
def
|
|
40
|
-
|
|
28
|
+
def inquire(mhead, mbody)
|
|
29
|
+
match = nil
|
|
30
|
+
sessx = nil
|
|
31
|
+
|
|
32
|
+
if mhead['subject'].include?('SMTP server: errors from ')
|
|
33
|
+
# src/smtpd/smtpd_chat.c:|337: post_mail_fprintf(notice, "Subject: %s SMTP server: errors from %s",
|
|
34
|
+
# src/smtpd/smtpd_chat.c:|338: var_mail_name, state->namaddr);
|
|
35
|
+
match = true
|
|
36
|
+
sessx = true
|
|
37
|
+
else
|
|
38
|
+
# Subject: Undelivered Mail Returned to Sender
|
|
39
|
+
match = true if mhead['subject'] == 'Undelivered Mail Returned to Sender'
|
|
40
|
+
end
|
|
41
|
+
return nil unless match
|
|
41
42
|
return nil if mhead['x-aol-ip']
|
|
42
43
|
|
|
43
|
-
require 'sisimai/rfc1894'
|
|
44
|
-
require 'sisimai/address'
|
|
45
|
-
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
|
46
44
|
permessage = {} # (Hash) Store values of each Per-Message field
|
|
47
|
-
|
|
48
45
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
|
49
|
-
|
|
50
|
-
bodyslices =
|
|
46
|
+
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
|
47
|
+
bodyslices = emailparts[0].split("\n")
|
|
51
48
|
readslices = ['']
|
|
52
|
-
readcursor = 0 # (Integer) Points the current cursor position
|
|
53
49
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
|
54
50
|
nomessages = false # (Boolean) Delivery report unavailable
|
|
55
51
|
commandset = [] # (Array) ``in reply to * command'' list
|
|
56
52
|
anotherset = {} # Another error information
|
|
57
53
|
v = nil
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
if sessx
|
|
56
|
+
# The message body starts with 'Transcript of session follows.'
|
|
57
|
+
require 'sisimai/smtp/transcript'
|
|
58
|
+
transcript = Sisimai::SMTP::Transcript.rise(emailparts[0], 'In:', 'Out:')
|
|
59
|
+
|
|
60
|
+
return nil unless transcript
|
|
61
|
+
return nil if transcript.size == 0
|
|
62
|
+
|
|
63
|
+
transcript.each do |e|
|
|
64
|
+
# Pick email addresses, error messages, and the last SMTP command.
|
|
65
|
+
v ||= dscontents[-1]
|
|
66
|
+
p = e['response']
|
|
67
|
+
|
|
68
|
+
if e['command'] == 'HELO' || e['command'] == 'EHLO'
|
|
69
|
+
# Use the argument of EHLO/HELO command as a value of "lhost"
|
|
70
|
+
v['lhost'] = e['argument']
|
|
71
|
+
|
|
72
|
+
elsif e['command'] == 'MAIL'
|
|
73
|
+
# Set the argument of "MAIL" command to pseudo To: header of the original message
|
|
74
|
+
emailparts[1] += sprintf("To: %s\n", e['argument']) if emailparts[1].size == 0
|
|
63
75
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
elsif e['command'] == 'RCPT'
|
|
77
|
+
# RCPT TO: <...>
|
|
78
|
+
if v['recipient']
|
|
79
|
+
# There are multiple recipient addresses in the transcript of session
|
|
80
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
|
81
|
+
v = dscontents[-1]
|
|
82
|
+
end
|
|
83
|
+
v['recipient'] = e['argument']
|
|
84
|
+
recipients += 1
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
next if p['reply'].to_i < 400
|
|
88
|
+
commandset << e['command']
|
|
89
|
+
v['diagnosis'] ||= p['text'].join(' ')
|
|
90
|
+
v['replycode'] ||= p['reply']
|
|
91
|
+
v['status'] ||= p['status']
|
|
68
92
|
end
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
#
|
|
80
|
-
if
|
|
93
|
+
else
|
|
94
|
+
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
|
95
|
+
readcursor = 0 # (Integer) Points the current cursor position
|
|
96
|
+
|
|
97
|
+
while e = bodyslices.shift do
|
|
98
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
|
99
|
+
# line of the beginning of the original message.
|
|
100
|
+
readslices << e # Save the current line for the next loop
|
|
101
|
+
|
|
102
|
+
if readcursor == 0
|
|
103
|
+
# Beginning of the bounce message or message/delivery-status part
|
|
104
|
+
readcursor |= Indicators[:deliverystatus] if StartingOf[:message].any? { |a| Sisimai::String.aligned(e, a) }
|
|
105
|
+
next
|
|
106
|
+
end
|
|
107
|
+
next if (readcursor & Indicators[:deliverystatus]) == 0
|
|
108
|
+
next if e.empty?
|
|
109
|
+
|
|
110
|
+
if f = Sisimai::RFC1894.match(e)
|
|
111
|
+
# "e" matched with any field defined in RFC3464
|
|
112
|
+
next unless o = Sisimai::RFC1894.field(e)
|
|
113
|
+
v = dscontents[-1]
|
|
114
|
+
|
|
115
|
+
if o[-1] == 'addr'
|
|
81
116
|
# Final-Recipient: rfc822; kijitora@example.jp
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
v
|
|
117
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
|
118
|
+
if o[0] == 'final-recipient'
|
|
119
|
+
# Final-Recipient: rfc822; kijitora@example.jp
|
|
120
|
+
if v['recipient']
|
|
121
|
+
# There are multiple recipient addresses in the message body.
|
|
122
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
|
123
|
+
v = dscontents[-1]
|
|
124
|
+
end
|
|
125
|
+
v['recipient'] = o[2]
|
|
126
|
+
recipients += 1
|
|
127
|
+
else
|
|
128
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
|
129
|
+
v['alias'] = o[2]
|
|
86
130
|
end
|
|
87
|
-
|
|
88
|
-
|
|
131
|
+
elsif o[-1] == 'code'
|
|
132
|
+
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
|
133
|
+
v['spec'] = o[1]
|
|
134
|
+
v['spec'] = 'SMTP' if v['spec'] == 'X-POSTFIX'
|
|
135
|
+
v['diagnosis'] = o[2]
|
|
89
136
|
else
|
|
90
|
-
#
|
|
91
|
-
|
|
137
|
+
# Other DSN fields defined in RFC3464
|
|
138
|
+
next unless fieldtable[o[0]]
|
|
139
|
+
v[fieldtable[o[0]]] = o[2]
|
|
140
|
+
|
|
141
|
+
next unless f
|
|
142
|
+
permessage[fieldtable[o[0]]] = o[2]
|
|
92
143
|
end
|
|
93
|
-
elsif o[-1] == 'code'
|
|
94
|
-
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
|
95
|
-
v['spec'] = o[1]
|
|
96
|
-
v['spec'] = 'SMTP' if v['spec'] == 'X-POSTFIX'
|
|
97
|
-
v['diagnosis'] = o[2]
|
|
98
144
|
else
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
145
|
+
# If you do so, please include this problem report. You can
|
|
146
|
+
# delete your own text from the attached returned message.
|
|
147
|
+
#
|
|
148
|
+
# The mail system
|
|
149
|
+
#
|
|
150
|
+
# <userunknown@example.co.jp>: host mx.example.co.jp[192.0.2.153] said: 550
|
|
151
|
+
# 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO command)
|
|
152
|
+
if readslices[-2].start_with?('Diagnostic-Code:') && e.include?(' ')
|
|
153
|
+
# Continued line of the value of Diagnostic-Code header
|
|
154
|
+
v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
|
|
155
|
+
readslices[-1] = 'Diagnostic-Code: ' << e
|
|
102
156
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
else
|
|
107
|
-
# If you do so, please include this problem report. You can
|
|
108
|
-
# delete your own text from the attached returned message.
|
|
109
|
-
#
|
|
110
|
-
# The mail system
|
|
111
|
-
#
|
|
112
|
-
# <userunknown@example.co.jp>: host mx.example.co.jp[192.0.2.153] said: 550
|
|
113
|
-
# 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO command)
|
|
114
|
-
if readslices[-2].start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
|
|
115
|
-
# Continued line of the value of Diagnostic-Code header
|
|
116
|
-
v['diagnosis'] << ' ' << cv[1]
|
|
117
|
-
readslices[-1] = 'Diagnostic-Code: ' << e
|
|
118
|
-
|
|
119
|
-
elsif cv = e.match(/\A(X-Postfix-Sender):[ ]*rfc822;[ ]*(.+)\z/)
|
|
120
|
-
# X-Postfix-Sender: rfc822; shironeko@example.org
|
|
121
|
-
emailsteak[1] << cv[1] << ': ' << cv[2] << "\n"
|
|
157
|
+
elsif Sisimai::String.aligned(e, ['X-Postfix-Sender:', 'rfc822;', '@'])
|
|
158
|
+
# X-Postfix-Sender: rfc822; shironeko@example.org
|
|
159
|
+
emailparts[1] << 'X-Postfix-Sender: ' << Sisimai::Address.s3s4(e[e.index(';') + 1, e.size]) << "\n"
|
|
122
160
|
|
|
123
|
-
else
|
|
124
|
-
if cv = e.match(/[ \t][(]in reply to (?:end of )?([A-Z]{4}).*/) ||
|
|
125
|
-
cv = e.match(/([A-Z]{4})[ \t]*.*command[)]\z/)
|
|
126
|
-
# 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
|
|
127
|
-
commandset << cv[1]
|
|
128
|
-
anotherset['diagnosis'] ||= ''
|
|
129
|
-
anotherset['diagnosis'] << ' ' << e
|
|
130
161
|
else
|
|
131
162
|
# Alternative error message and recipient
|
|
132
|
-
if
|
|
163
|
+
if e.include?(' (in reply to ') || e.include?('command)')
|
|
164
|
+
# 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
|
|
165
|
+
q = Sisimai::SMTP::Command.find(e); commandset << q if q
|
|
166
|
+
anotherset['diagnosis'] ||= ''
|
|
167
|
+
anotherset['diagnosis'] << ' ' << e
|
|
168
|
+
|
|
169
|
+
elsif Sisimai::String.aligned(e, ['<', '@', '>', '(expanded from<', '):'])
|
|
133
170
|
# <r@example.ne.jp> (expanded from <kijitora@example.org>): user ...
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
171
|
+
p1 = e.index('> ')
|
|
172
|
+
p2 = e.index('(expanded from ', p1)
|
|
173
|
+
p3 = e.index('>): ', p2 + 14)
|
|
174
|
+
anotherset['recipient'] = Sisimai::Address.s3s4(e[0, p1])
|
|
175
|
+
anotherset['alias'] = Sisimai::Address.s3s4(e[p2 + 15, p3 - p2 - 15])
|
|
176
|
+
anotherset['diagnosis'] = e[p3 + 3, e.size]
|
|
137
177
|
|
|
138
|
-
elsif
|
|
178
|
+
elsif e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>:'])
|
|
139
179
|
# <kijitora@exmaple.jp>: ...
|
|
140
|
-
anotherset['recipient'] =
|
|
141
|
-
anotherset['diagnosis'] =
|
|
180
|
+
anotherset['recipient'] = Sisimai::Address.s3s4(e[0, e.index('>')])
|
|
181
|
+
anotherset['diagnosis'] = e[e.index('>:') + 2, e.size]
|
|
142
182
|
|
|
143
183
|
elsif e.include?('--- Delivery report unavailable ---')
|
|
144
184
|
# postfix-3.1.4/src/bounce/bounce_notify_util.c
|
|
@@ -152,16 +192,17 @@ module Sisimai::Lhost
|
|
|
152
192
|
# bounce_notify_util.c:602|} else {
|
|
153
193
|
nomessages = true
|
|
154
194
|
else
|
|
155
|
-
# Get error message continued from the previous line
|
|
195
|
+
# Get an error message continued from the previous line
|
|
156
196
|
next unless anotherset['diagnosis']
|
|
157
|
-
if e
|
|
197
|
+
if e.start_with?(' ')
|
|
158
198
|
# host mx.example.jp said:...
|
|
159
|
-
anotherset['diagnosis'] << ' ' << e
|
|
199
|
+
anotherset['diagnosis'] << ' ' << e[4, e.size]
|
|
160
200
|
end
|
|
161
201
|
end
|
|
162
202
|
end
|
|
163
203
|
end
|
|
164
|
-
end
|
|
204
|
+
end # end of while()
|
|
205
|
+
|
|
165
206
|
end
|
|
166
207
|
|
|
167
208
|
unless recipients > 0
|
|
@@ -171,12 +212,13 @@ module Sisimai::Lhost
|
|
|
171
212
|
dscontents[-1]['recipient'] = anotherset['recipient']
|
|
172
213
|
recipients += 1
|
|
173
214
|
else
|
|
174
|
-
# Get a recipient address from message/rfc822 part if the delivery
|
|
175
|
-
#
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
215
|
+
# Get a recipient address from message/rfc822 part if the delivery report was unavailable:
|
|
216
|
+
# '--- Delivery report unavailable ---'
|
|
217
|
+
p1 = emailparts[1].index("\nTo: ") || -1
|
|
218
|
+
p2 = emailparts[1].index("\n", p1 + 6) || -1
|
|
219
|
+
if nomessages && p1 > 0
|
|
220
|
+
# Try to get a recipient address from To: field in the original message at message/rfc822 part
|
|
221
|
+
dscontents[-1]['recipient'] = Sisimai::Address.s3s4(emailparts[1][p1 + 5, p2 - p1 - 5])
|
|
180
222
|
recipients += 1
|
|
181
223
|
end
|
|
182
224
|
end
|
|
@@ -185,55 +227,62 @@ module Sisimai::Lhost
|
|
|
185
227
|
|
|
186
228
|
dscontents.each do |e|
|
|
187
229
|
# Set default values if each value is empty.
|
|
188
|
-
e['lhost'] ||= permessage['rhost']
|
|
189
230
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
|
190
231
|
|
|
191
232
|
if anotherset['diagnosis']
|
|
192
233
|
# Copy alternative error message
|
|
193
|
-
|
|
234
|
+
anotherset['diagnosis'] = Sisimai::String.sweep(anotherset['diagnosis'])
|
|
235
|
+
e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].nil? || e['diagnosis'].empty?
|
|
194
236
|
|
|
195
237
|
if e['diagnosis'] =~ /\A\d+\z/
|
|
238
|
+
# Override the value of diagnostic code message
|
|
196
239
|
e['diagnosis'] = anotherset['diagnosis']
|
|
197
240
|
else
|
|
198
241
|
# More detailed error message is in "anotherset"
|
|
199
|
-
as =
|
|
200
|
-
ar =
|
|
242
|
+
as = '' # status
|
|
243
|
+
ar = '' # replycode
|
|
201
244
|
|
|
202
245
|
e['status'] ||= ''
|
|
203
246
|
e['replycode'] ||= ''
|
|
204
247
|
|
|
205
|
-
if e['status']
|
|
248
|
+
if e['status'].empty? || e['status'].start_with?('4.0.0', '5.0.0')
|
|
206
249
|
# Check the value of D.S.N. in anotherset
|
|
207
|
-
as = Sisimai::SMTP::Status.find(anotherset['diagnosis'])
|
|
208
|
-
if as && as[-
|
|
250
|
+
as = Sisimai::SMTP::Status.find(anotherset['diagnosis']) || ''
|
|
251
|
+
if as.size > 0 && as[-4, 4] != '.0.0'
|
|
209
252
|
# The D.S.N. is neither an empty nor *.0.0
|
|
210
253
|
e['status'] = as
|
|
211
254
|
end
|
|
212
255
|
end
|
|
213
256
|
|
|
214
|
-
if e['replycode']
|
|
215
|
-
# Check the value of SMTP reply code in anotherset
|
|
216
|
-
ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis'])
|
|
217
|
-
if ar && ar
|
|
257
|
+
if e['replycode'].empty? || e['replycode'].end_with?('00')
|
|
258
|
+
# Check the value of SMTP reply code in $anotherset
|
|
259
|
+
ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis']) || ''
|
|
260
|
+
if ar.size > 0 && ar.end_with?('00') == false
|
|
218
261
|
# The SMTP reply code is neither an empty nor *00
|
|
219
262
|
e['replycode'] = ar
|
|
220
263
|
end
|
|
221
264
|
end
|
|
222
265
|
|
|
223
|
-
|
|
224
|
-
#
|
|
266
|
+
while true
|
|
267
|
+
# Replace e['diagnosis'] with the value of anotherset['diagnosis'] when all the
|
|
268
|
+
# following conditions have not matched.
|
|
269
|
+
break if (as + ar).size == 0
|
|
270
|
+
break if anotherset['diagnosis'].size < e['diagnosis'].size
|
|
271
|
+
break if anotherset['diagnosis'].include?(e['diagnosis']) == false
|
|
272
|
+
|
|
225
273
|
e['diagnosis'] = anotherset['diagnosis']
|
|
274
|
+
break
|
|
226
275
|
end
|
|
227
276
|
end
|
|
228
277
|
end
|
|
229
278
|
|
|
230
|
-
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
|
231
|
-
e['command'] = commandset.shift ||
|
|
232
|
-
e['command'] ||= 'HELO' if e['diagnosis']
|
|
233
|
-
e['spec'] ||= 'SMTP' if e['diagnosis']
|
|
279
|
+
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
|
|
280
|
+
e['command'] = commandset.shift || Sisimai::SMTP::Command.find(e['diagnosis'])
|
|
281
|
+
e['command'] ||= 'HELO' if e['diagnosis'].include?('refused to talk to me:')
|
|
282
|
+
e['spec'] ||= 'SMTP' if Sisimai::String.aligned(e['diagnosis'], ['host ', ' said:'])
|
|
234
283
|
end
|
|
235
284
|
|
|
236
|
-
return { 'ds' => dscontents, 'rfc822' =>
|
|
285
|
+
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
|
237
286
|
end
|
|
238
287
|
def description; return 'Postfix'; end
|
|
239
288
|
end
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
module Sisimai::Lhost
|
|
2
|
-
# Sisimai::Lhost::PowerMTA parses a bounce email which created by
|
|
3
|
-
#
|
|
2
|
+
# Sisimai::Lhost::PowerMTA parses a bounce email which created by PowerMTA. Methods in the module
|
|
3
|
+
# are called from only Sisimai::Message.
|
|
4
4
|
module PowerMTA
|
|
5
5
|
class << self
|
|
6
|
-
# Imported from p5-Sisimail/lib/Sisimai/Lhost/PowerMTA.pm
|
|
7
6
|
require 'sisimai/lhost'
|
|
8
7
|
|
|
9
8
|
Indicators = Sisimai::Lhost.INDICATORS
|
|
10
|
-
|
|
9
|
+
Boundaries = ['Content-Type: text/rfc822-headers'].freeze
|
|
11
10
|
StartingOf = { message: ['Hello, this is the mail server on '] }.freeze
|
|
12
11
|
Categories = {
|
|
13
12
|
'bad-domain' => 'hostunknown',
|
|
@@ -27,23 +26,22 @@ module Sisimai::Lhost
|
|
|
27
26
|
# @return [Hash] Bounce data list and message/rfc822 part
|
|
28
27
|
# @return [Nil] it failed to parse or the arguments are missing
|
|
29
28
|
# @since v4.25.6
|
|
30
|
-
def
|
|
29
|
+
def inquire(mhead, mbody)
|
|
31
30
|
return nil unless mhead['subject'].to_s.start_with?('Delivery report')
|
|
32
31
|
|
|
33
|
-
require 'sisimai/rfc1894'
|
|
34
32
|
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
|
35
33
|
permessage = {} # (Hash) Store values of each Per-Message field
|
|
36
34
|
|
|
37
35
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
|
38
|
-
|
|
39
|
-
bodyslices =
|
|
36
|
+
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
|
37
|
+
bodyslices = emailparts[0].split("\n")
|
|
40
38
|
readcursor = 0 # (Integer) Points the current cursor position
|
|
41
39
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
|
42
40
|
v = nil
|
|
43
41
|
|
|
44
42
|
while e = bodyslices.shift do
|
|
45
|
-
# Read error messages and delivery status lines from the head of the email
|
|
46
|
-
#
|
|
43
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
|
44
|
+
# line of the beginning of the original message.
|
|
47
45
|
if readcursor == 0
|
|
48
46
|
# Beginning of the bounce message or message/delivery-status part
|
|
49
47
|
readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
|
|
@@ -82,7 +80,7 @@ module Sisimai::Lhost
|
|
|
82
80
|
next unless fieldtable[o[0]]
|
|
83
81
|
v[fieldtable[o[0]]] = o[2]
|
|
84
82
|
|
|
85
|
-
next unless f
|
|
83
|
+
next unless f
|
|
86
84
|
permessage[fieldtable[o[0]]] = o[2]
|
|
87
85
|
end
|
|
88
86
|
else
|
|
@@ -96,9 +94,9 @@ module Sisimai::Lhost
|
|
|
96
94
|
#
|
|
97
95
|
# <kijitora@example.jp> delivery failed; will not continue trying
|
|
98
96
|
#
|
|
99
|
-
if
|
|
97
|
+
if e.start_with?('X-PowerMTA-BounceCategory: ')
|
|
100
98
|
# X-PowerMTA-BounceCategory: bad-mailbox
|
|
101
|
-
v['category'] =
|
|
99
|
+
v['category'] = e[e.index(':') + 2, e.size]
|
|
102
100
|
end
|
|
103
101
|
end
|
|
104
102
|
end
|
|
@@ -111,7 +109,7 @@ module Sisimai::Lhost
|
|
|
111
109
|
e['reason'] = Categories[e['category']] || ''
|
|
112
110
|
end
|
|
113
111
|
|
|
114
|
-
return { 'ds' => dscontents, 'rfc822' =>
|
|
112
|
+
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
|
115
113
|
end
|
|
116
114
|
def description; return 'PowerMTA: https://www.sparkpost.com/powermta/'; end
|
|
117
115
|
end
|