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,13 +1,15 @@
|
|
|
1
1
|
module Sisimai::Lhost
|
|
2
|
-
# Sisimai::Lhost::Sendmail parses a bounce email which created by
|
|
3
|
-
#
|
|
2
|
+
# Sisimai::Lhost::Sendmail parses a bounce email which created by v8 Sendmail. Methods in the module
|
|
3
|
+
# are called from only Sisimai::Message.
|
|
4
4
|
module Sendmail
|
|
5
5
|
class << self
|
|
6
|
-
# Imported from p5-Sisimail/lib/Sisimai/Lhost/Sendmail.pm
|
|
7
6
|
require 'sisimai/lhost'
|
|
7
|
+
require 'sisimai/smtp/reply'
|
|
8
|
+
require 'sisimai/smtp/status'
|
|
9
|
+
require 'sisimai/smtp/command'
|
|
8
10
|
|
|
9
11
|
Indicators = Sisimai::Lhost.INDICATORS
|
|
10
|
-
|
|
12
|
+
Boundaries = ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'].freeze
|
|
11
13
|
StartingOf = {
|
|
12
14
|
# Error text regular expressions which defined in sendmail/savemail.c
|
|
13
15
|
# savemail.c:1040|if (printheader && !putline(" ----- Transcript of session follows -----\n",
|
|
@@ -26,29 +28,30 @@ module Sisimai::Lhost
|
|
|
26
28
|
# @param [String] mbody Message body of a bounce email
|
|
27
29
|
# @return [Hash] Bounce data list and message/rfc822 part
|
|
28
30
|
# @return [Nil] it failed to parse or the arguments are missing
|
|
29
|
-
def
|
|
30
|
-
return nil unless mhead['subject'] =~ /(?:see transcript for details\z|\AWarning: )/
|
|
31
|
+
def inquire(mhead, mbody)
|
|
31
32
|
return nil if mhead['x-aol-ip']
|
|
33
|
+
match = nil
|
|
34
|
+
match ||= true if mhead['subject'].end_with?('see transcript for details')
|
|
35
|
+
match ||= true if mhead['subject'].start_with?('Warning: ')
|
|
36
|
+
return nil unless match
|
|
32
37
|
|
|
33
|
-
require 'sisimai/rfc1894'
|
|
34
38
|
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
|
35
39
|
permessage = {} # (Hash) Store values of each Per-Message field
|
|
36
|
-
|
|
37
40
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
|
38
|
-
|
|
39
|
-
bodyslices =
|
|
41
|
+
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
|
42
|
+
bodyslices = emailparts[0].split("\n")
|
|
40
43
|
readslices = ['']
|
|
41
44
|
readcursor = 0 # (Integer) Points the current cursor position
|
|
42
45
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
|
43
|
-
|
|
46
|
+
thecommand = '' # (String) SMTP Command name begin with the string '>>>'
|
|
44
47
|
esmtpreply = [] # (Array) Reply from remote server on SMTP session
|
|
45
48
|
sessionerr = false # (Boolean) Flag, "true" if it is SMTP session error
|
|
46
49
|
anotherset = {} # Another error information
|
|
47
50
|
v = nil
|
|
48
51
|
|
|
49
52
|
while e = bodyslices.shift do
|
|
50
|
-
# Read error messages and delivery status lines from the head of the email
|
|
51
|
-
#
|
|
53
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
|
54
|
+
# line of the beginning of the original message.
|
|
52
55
|
readslices << e # Save the current line for the next loop
|
|
53
56
|
|
|
54
57
|
if readcursor == 0
|
|
@@ -89,7 +92,7 @@ module Sisimai::Lhost
|
|
|
89
92
|
next unless fieldtable[o[0]]
|
|
90
93
|
v[fieldtable[o[0]]] = o[2]
|
|
91
94
|
|
|
92
|
-
next unless f
|
|
95
|
+
next unless f
|
|
93
96
|
permessage[fieldtable[o[0]]] = o[2]
|
|
94
97
|
end
|
|
95
98
|
else
|
|
@@ -105,13 +108,14 @@ module Sisimai::Lhost
|
|
|
105
108
|
# Received-From-MTA: DNS; x1x2x3x4.dhcp.example.ne.jp
|
|
106
109
|
# Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
|
|
107
110
|
unless e.start_with?(' ')
|
|
108
|
-
if
|
|
111
|
+
if e.start_with?('>>> ')
|
|
109
112
|
# >>> DATA
|
|
110
|
-
|
|
113
|
+
thecommand = Sisimai::SMTP::Command.find(e)
|
|
111
114
|
|
|
112
|
-
elsif
|
|
115
|
+
elsif e.start_with?('<<< ')
|
|
113
116
|
# <<< Response
|
|
114
|
-
|
|
117
|
+
cv = e[4, e.size - 4]
|
|
118
|
+
esmtpreply << cv unless esmtpreply.index(cv)
|
|
115
119
|
else
|
|
116
120
|
# Detect SMTP session error or connection error
|
|
117
121
|
next if sessionerr
|
|
@@ -122,21 +126,23 @@ module Sisimai::Lhost
|
|
|
122
126
|
next
|
|
123
127
|
end
|
|
124
128
|
|
|
125
|
-
if
|
|
129
|
+
if e.start_with?('<') && Sisimai::String.aligned(e, ['@', '>.', ' '])
|
|
126
130
|
# <kijitora@example.co.jp>... Deferred: Name server: example.co.jp.: host name lookup failure
|
|
127
|
-
anotherset['recipient'] =
|
|
128
|
-
anotherset['diagnosis'] =
|
|
131
|
+
anotherset['recipient'] = Sisimai::Address.s3s4(e[0, e.index('>')])
|
|
132
|
+
anotherset['diagnosis'] = e[e.index(' ') + 1, e.size]
|
|
129
133
|
else
|
|
130
134
|
# ----- Transcript of session follows -----
|
|
131
135
|
# Message could not be delivered for too long
|
|
132
136
|
# Message will be deleted from queue
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
cr = Sisimai::SMTP::Reply.find(e) || ''
|
|
138
|
+
cs = Sisimai::SMTP::Status.find(e) || ''
|
|
139
|
+
|
|
140
|
+
if cr.size + cs.size > 7
|
|
135
141
|
# 550 5.1.2 <kijitora@example.org>... Message
|
|
136
142
|
#
|
|
137
143
|
# DBI connect('dbname=...')
|
|
138
144
|
# 554 5.3.0 unknown mailer error 255
|
|
139
|
-
anotherset['status']
|
|
145
|
+
anotherset['status'] = cs
|
|
140
146
|
anotherset['diagnosis'] ||= ''
|
|
141
147
|
anotherset['diagnosis'] << ' ' << e
|
|
142
148
|
|
|
@@ -151,8 +157,8 @@ module Sisimai::Lhost
|
|
|
151
157
|
else
|
|
152
158
|
# Continued line of the value of Diagnostic-Code field
|
|
153
159
|
next unless readslices[-2].start_with?('Diagnostic-Code:')
|
|
154
|
-
next unless
|
|
155
|
-
v['diagnosis'] << ' ' <<
|
|
160
|
+
next unless e.start_with?(' ')
|
|
161
|
+
v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
|
|
156
162
|
readslices[-1] = 'Diagnostic-Code: ' << e
|
|
157
163
|
end
|
|
158
164
|
end
|
|
@@ -161,47 +167,53 @@ module Sisimai::Lhost
|
|
|
161
167
|
|
|
162
168
|
dscontents.each do |e|
|
|
163
169
|
# Set default values if each value is empty.
|
|
164
|
-
e['
|
|
170
|
+
e['diagnosis'] ||= ''
|
|
165
171
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
|
166
172
|
|
|
167
|
-
e['command'] ||= commandtxt
|
|
168
|
-
if e['command'].empty?
|
|
169
|
-
e['command'] = 'EHLO' unless esmtpreply.empty?
|
|
170
|
-
end
|
|
171
|
-
|
|
172
173
|
if anotherset['diagnosis']
|
|
173
174
|
# Copy alternative error message
|
|
174
|
-
e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis']
|
|
175
|
-
e['diagnosis'] = anotherset['diagnosis']
|
|
176
|
-
e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis']
|
|
175
|
+
e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].start_with?(' ')
|
|
176
|
+
e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].=~ /\A\d+\z/
|
|
177
|
+
e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].empty?
|
|
177
178
|
end
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
|
|
180
|
+
while true
|
|
181
|
+
# Replace or append the error message in "diagnosis" with the ESMTP Reply Code when the
|
|
182
|
+
# following conditions have matched
|
|
183
|
+
break if esmtpreply.empty?
|
|
184
|
+
break if recipients != 1
|
|
185
|
+
|
|
186
|
+
e['diagnosis'] = sprintf("%s %s", esmtpreply.join(' '), e['diagnosis'])
|
|
187
|
+
break
|
|
182
188
|
end
|
|
183
|
-
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
|
184
189
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
e['status'] = anotherset['status']
|
|
190
|
-
end
|
|
190
|
+
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
|
191
|
+
e['command'] ||= thecommand || Sisimai::SMTP::Command.find(e['diagnosis']) || ''
|
|
192
|
+
if e['command'].empty?
|
|
193
|
+
e['command'] = 'EHLO' unless esmtpreply.empty?
|
|
191
194
|
end
|
|
192
195
|
|
|
193
|
-
|
|
194
|
-
#
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
while true
|
|
197
|
+
# Check alternative status code and override it
|
|
198
|
+
break unless anotherset.has_key?('status')
|
|
199
|
+
break unless anotherset['status'].size > 0
|
|
200
|
+
break if Sisimai::SMTP::Status.test(e['status'])
|
|
201
|
+
|
|
202
|
+
e['status'] = anotherset['status']
|
|
203
|
+
break
|
|
199
204
|
end
|
|
205
|
+
|
|
206
|
+
# @example.jp, no local part
|
|
207
|
+
# # Get email address from the value of Diagnostic-Code field
|
|
208
|
+
next unless e['recipient'].start_with?('@')
|
|
209
|
+
cv = Sisimai::Address.find(e['diagnosis'], true) || []
|
|
210
|
+
e['recipient'] = cv[0][:address] if cv.size > 0
|
|
200
211
|
end
|
|
201
212
|
|
|
202
|
-
return { 'ds' => dscontents, 'rfc822' =>
|
|
213
|
+
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
|
203
214
|
end
|
|
204
215
|
def description; return 'V8Sendmail: /usr/sbin/sendmail'; end
|
|
205
216
|
end
|
|
206
217
|
end
|
|
207
218
|
end
|
|
219
|
+
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
module Sisimai::Lhost
|
|
2
|
-
# Sisimai::Lhost::SurfControl parses a bounce email which created by
|
|
3
|
-
# WebSense SurfControl.
|
|
2
|
+
# Sisimai::Lhost::SurfControl parses a bounce email which created by WebSense SurfControl.
|
|
4
3
|
# Methods in the module are called from only Sisimai::Message.
|
|
5
4
|
module SurfControl
|
|
6
5
|
class << self
|
|
7
|
-
# Imported from p5-Sisimail/lib/Sisimai/Lhost/SurfControl.pm
|
|
8
6
|
require 'sisimai/lhost'
|
|
9
7
|
|
|
10
8
|
Indicators = Sisimai::Lhost.INDICATORS
|
|
11
|
-
|
|
9
|
+
Boundaries = ['Content-Type: message/rfc822'].freeze
|
|
12
10
|
StartingOf = { message: ['Your message could not be sent.'] }.freeze
|
|
13
11
|
|
|
14
12
|
# Parse bounce messages from SurfControl
|
|
@@ -16,7 +14,7 @@ module Sisimai::Lhost
|
|
|
16
14
|
# @param [String] mbody Message body of a bounce email
|
|
17
15
|
# @return [Hash] Bounce data list and message/rfc822 part
|
|
18
16
|
# @return [Nil] it failed to parse or the arguments are missing
|
|
19
|
-
def
|
|
17
|
+
def inquire(mhead, mbody)
|
|
20
18
|
# X-SEF-ZeroHour-RefID: fgs=000000000
|
|
21
19
|
# X-SEF-Processed: 0_0_0_000__2010_04_29_23_34_45
|
|
22
20
|
# X-Mailer: SurfControl E-mail Filter
|
|
@@ -24,19 +22,18 @@ module Sisimai::Lhost
|
|
|
24
22
|
return nil unless mhead['x-mailer']
|
|
25
23
|
return nil unless mhead['x-mailer'] == 'SurfControl E-mail Filter'
|
|
26
24
|
|
|
27
|
-
require 'sisimai/rfc1894'
|
|
28
25
|
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
|
29
26
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
|
30
|
-
|
|
31
|
-
bodyslices =
|
|
27
|
+
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
|
28
|
+
bodyslices = emailparts[0].split("\n")
|
|
32
29
|
readslices = ['']
|
|
33
30
|
readcursor = 0 # (Integer) Points the current cursor position
|
|
34
31
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
|
35
32
|
v = nil
|
|
36
33
|
|
|
37
34
|
while e = bodyslices.shift do
|
|
38
|
-
# Read error messages and delivery status lines from the head of the email
|
|
39
|
-
#
|
|
35
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
|
36
|
+
# line of the beginning of the original message.
|
|
40
37
|
readslices << e # Save the current line for the next loop
|
|
41
38
|
|
|
42
39
|
if readcursor == 0
|
|
@@ -58,24 +55,27 @@ module Sisimai::Lhost
|
|
|
58
55
|
# --- Message non-deliverable.
|
|
59
56
|
v = dscontents[-1]
|
|
60
57
|
|
|
61
|
-
if
|
|
58
|
+
if e.start_with?('Addressed To:') && e.index('@') > 1
|
|
62
59
|
# Addressed To: kijitora@example.com
|
|
63
60
|
if v['recipient']
|
|
64
61
|
# There are multiple recipient addresses in the message body.
|
|
65
62
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
|
66
63
|
v = dscontents[-1]
|
|
67
64
|
end
|
|
68
|
-
v['recipient'] =
|
|
65
|
+
v['recipient'] = Sisimai::Address.s3s4(e[e.index(':') + 2, e.size])
|
|
69
66
|
recipients += 1
|
|
70
67
|
|
|
71
|
-
elsif
|
|
68
|
+
elsif %w[Sun Mon Tue Wed Thu Fri Sat].any? { |a| e.start_with?(a) }
|
|
72
69
|
# Thu 29 Apr 2010 23:34:45 +0900
|
|
73
70
|
v['date'] = e
|
|
74
71
|
|
|
75
|
-
elsif
|
|
72
|
+
elsif Sisimai::String.aligned(e, ['@', ':', ' ', '[', '],', '...'])
|
|
76
73
|
# kijitora@example.com: [192.0.2.5], 550 kijitora@example.com... No such user
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
p1 = e.index('[')
|
|
75
|
+
p2 = e.index('],', p1 + 1)
|
|
76
|
+
v['rhost'] = e[p1 + 1, p2 - p1 - 1]
|
|
77
|
+
v['diagnosis'] = Sisimai::String.sweep(e[p2 + 2, e.size])
|
|
78
|
+
|
|
79
79
|
else
|
|
80
80
|
# Fallback, parse RFC3464 headers.
|
|
81
81
|
if f = Sisimai::RFC1894.match(e)
|
|
@@ -87,8 +87,8 @@ module Sisimai::Lhost
|
|
|
87
87
|
else
|
|
88
88
|
# Continued line of the value of Diagnostic-Code field
|
|
89
89
|
next unless readslices[-2].start_with?('Diagnostic-Code:')
|
|
90
|
-
next unless
|
|
91
|
-
v['diagnosis'] << ' ' <<
|
|
90
|
+
next unless e.start_with?(' ')
|
|
91
|
+
v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
|
|
92
92
|
readslices[-1] = 'Diagnostic-Code: ' << e
|
|
93
93
|
end
|
|
94
94
|
end
|
|
@@ -96,7 +96,7 @@ module Sisimai::Lhost
|
|
|
96
96
|
return nil unless recipients > 0
|
|
97
97
|
|
|
98
98
|
dscontents.each { |e| e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) }
|
|
99
|
-
return { 'ds' => dscontents, 'rfc822' =>
|
|
99
|
+
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
|
100
100
|
end
|
|
101
101
|
def description; return 'WebSense SurfControl'; end
|
|
102
102
|
end
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
module Sisimai::Lhost
|
|
2
|
-
# Sisimai::Lhost::V5sendmail parses a bounce email which created by
|
|
3
|
-
#
|
|
4
|
-
# Methods in the module are called from only Sisimai::Message.
|
|
2
|
+
# Sisimai::Lhost::V5sendmail parses a bounce email which created by Sendmail version 5. Methods in
|
|
3
|
+
# the module are called from only Sisimai::Message.
|
|
5
4
|
module V5sendmail
|
|
6
5
|
class << self
|
|
7
|
-
# Imported from p5-Sisimail/lib/Sisimai/Lhost/V5sendmail.pm
|
|
8
6
|
require 'sisimai/lhost'
|
|
9
7
|
|
|
10
8
|
Indicators = Sisimai::Lhost.INDICATORS
|
|
11
|
-
|
|
12
|
-
StartingOf = {
|
|
13
|
-
MarkingsOf = {
|
|
9
|
+
Boundaries = [' ----- Unsent message follows -----', ' ----- No message was collected -----'].freeze
|
|
10
|
+
StartingOf = {
|
|
14
11
|
# Error text regular expressions which defined in src/savemail.c
|
|
15
12
|
# savemail.c:485| (void) fflush(stdout);
|
|
16
13
|
# savemail.c:486| p = queuename(e->e_parent, 'x');
|
|
@@ -27,7 +24,8 @@ module Sisimai::Lhost
|
|
|
27
24
|
# savemail.c:497| while (fgets(buf, sizeof buf, xfile) != NULL)
|
|
28
25
|
# savemail.c:498| putline(buf, fp, m);
|
|
29
26
|
# savemail.c:499| (void) fclose(xfile);
|
|
30
|
-
error:
|
|
27
|
+
error: [' while talking to '],
|
|
28
|
+
message: ['----- Transcript of session follows -----'],
|
|
31
29
|
}.freeze
|
|
32
30
|
|
|
33
31
|
# Parse bounce messages from Sendmail version 5
|
|
@@ -35,26 +33,27 @@ module Sisimai::Lhost
|
|
|
35
33
|
# @param [String] mbody Message body of a bounce email
|
|
36
34
|
# @return [Hash] Bounce data list and message/rfc822 part
|
|
37
35
|
# @return [Nil] it failed to parse or the arguments are missing
|
|
38
|
-
def
|
|
36
|
+
def inquire(mhead, mbody)
|
|
39
37
|
# :from => %r/\AMail Delivery Subsystem/,
|
|
40
|
-
return nil unless mhead['subject']
|
|
38
|
+
return nil unless mhead['subject'].start_with?('Returned mail: ')
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
return nil
|
|
40
|
+
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
|
41
|
+
return nil unless emailparts[1].size > 0
|
|
44
42
|
|
|
43
|
+
require 'sisimai/smtp/command'
|
|
45
44
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
|
46
|
-
bodyslices =
|
|
45
|
+
bodyslices = emailparts[0].split("\n")
|
|
47
46
|
readcursor = 0 # (Integer) Points the current cursor position
|
|
48
47
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
|
48
|
+
anotherset = {} # (Hash) Another error information
|
|
49
49
|
responding = [] # (Array) Responses from remote server
|
|
50
50
|
commandset = [] # (Array) SMTP command which is sent to remote server
|
|
51
|
-
anotherset = {} # (Hash) Another error information
|
|
52
51
|
errorindex = -1 # (Integer)
|
|
53
52
|
v = nil
|
|
54
53
|
|
|
55
54
|
while e = bodyslices.shift do
|
|
56
|
-
# Read error messages and delivery status lines from the head of the email
|
|
57
|
-
#
|
|
55
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
|
56
|
+
# line of the beginning of the original message.
|
|
58
57
|
if readcursor == 0
|
|
59
58
|
# Beginning of the bounce message or delivery status part
|
|
60
59
|
readcursor |= Indicators[:deliverystatus] if e.include?(StartingOf[:message][0])
|
|
@@ -71,57 +70,64 @@ module Sisimai::Lhost
|
|
|
71
70
|
# 421 example.org (smtp)... Deferred: Connection timed out during user open with example.org
|
|
72
71
|
v = dscontents[-1]
|
|
73
72
|
|
|
74
|
-
if
|
|
73
|
+
if e.start_with?('5', '4') && Sisimai::String.aligned(e, [' <', '@', '>...'])
|
|
75
74
|
# 550 <kijitora@example.org>... User unknown
|
|
76
75
|
if v['recipient']
|
|
77
76
|
# There are multiple recipient addresses in the message body.
|
|
78
77
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
|
79
78
|
v = dscontents[-1]
|
|
80
79
|
end
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
p1 = e.index('<', 0)
|
|
81
|
+
p2 = e.index('>...')
|
|
82
|
+
v['recipient'] = e[p1 + 1, p2 - p1 - 1]
|
|
83
|
+
v['diagnosis'] = e[p2 + 5, e.size]
|
|
83
84
|
|
|
84
85
|
# Concatenate the response of the server and error message
|
|
85
86
|
v['diagnosis'] << ': ' << responding[recipients] if responding[recipients]
|
|
86
87
|
recipients += 1
|
|
87
88
|
|
|
88
|
-
elsif
|
|
89
|
+
elsif e.start_with?('>>> ')
|
|
89
90
|
# >>> RCPT To:<kijitora@example.org>
|
|
90
|
-
commandset[recipients] = cv
|
|
91
|
+
cv = Sisimai::SMTP::Command.find(e); commandset[recipients] = cv if cv
|
|
91
92
|
|
|
92
|
-
elsif
|
|
93
|
+
elsif e.start_with?('<<< ')
|
|
93
94
|
# <<< Response
|
|
94
95
|
# <<< 501 <shironeko@example.co.jp>... no access from mail server [192.0.2.55] which is an open relay.
|
|
95
96
|
# <<< 550 Requested User Mailbox not found. No such user here.
|
|
96
|
-
responding[recipients] =
|
|
97
|
+
responding[recipients] = e[4, e.size]
|
|
98
|
+
|
|
97
99
|
else
|
|
98
100
|
# Detect SMTP session error or connection error
|
|
99
101
|
next if v['sessionerr']
|
|
100
102
|
|
|
101
|
-
if e
|
|
103
|
+
if e.include?(StartingOf[:error][0])
|
|
102
104
|
# ----- Transcript of session follows -----
|
|
103
105
|
# ... while talking to mta.example.org.:
|
|
104
106
|
v['sessionerr'] = true
|
|
105
107
|
next
|
|
106
108
|
end
|
|
107
109
|
|
|
108
|
-
if
|
|
110
|
+
if e.start_with?('4', '5') && e.include?('... ')
|
|
109
111
|
# 421 example.org (smtp)... Deferred: Connection timed out during user open with example.org
|
|
110
|
-
anotherset['
|
|
112
|
+
anotherset['replycode'] = e[0, 3]
|
|
113
|
+
anotherset['diagnosis'] = e[e.index('... ') + 4, e.size]
|
|
111
114
|
end
|
|
112
115
|
end
|
|
113
116
|
end
|
|
114
117
|
|
|
115
|
-
|
|
118
|
+
p1 = emailparts[1].index("\nTo: ") || -1
|
|
119
|
+
p2 = emailparts[1].index("\n", p1 + 6) || -1
|
|
120
|
+
if recipients == 0 && p1 > 0
|
|
116
121
|
# Get the recipient address from "To:" header at the original message
|
|
117
|
-
dscontents[0]['recipient'] = Sisimai::Address.s3s4(
|
|
122
|
+
dscontents[0]['recipient'] = Sisimai::Address.s3s4(emailparts[1][p1, p2 - p1 - 5])
|
|
118
123
|
recipients = 1
|
|
119
124
|
end
|
|
120
125
|
return nil unless recipients > 0
|
|
121
126
|
|
|
122
127
|
dscontents.each do |e|
|
|
123
128
|
errorindex += 1
|
|
124
|
-
e
|
|
129
|
+
e.delete('sessionerr')
|
|
130
|
+
|
|
125
131
|
e['diagnosis'] ||= if anotherset['diagnosis'].to_s.size > 0
|
|
126
132
|
# Copy alternative error message
|
|
127
133
|
anotherset['diagnosis']
|
|
@@ -130,18 +136,18 @@ module Sisimai::Lhost
|
|
|
130
136
|
responding[errorindex]
|
|
131
137
|
end
|
|
132
138
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
e.
|
|
139
|
+
e['replycode'] = Sisimai::SMTP::Reply.find(e['diagnosis']) || anotherset['replycode']
|
|
140
|
+
e['command'] = commandset[errorindex] || Sisimai::SMTP::Command.find(e['diagnosis']) || ''
|
|
141
|
+
|
|
142
|
+
# @example.jp, no local part
|
|
143
|
+
# Get email address from the value of Diagnostic-Code header
|
|
144
|
+
next if e['recipient'].include?('@')
|
|
145
|
+
p1 = e['diagnosis'].index('<'); next unless p1
|
|
146
|
+
p2 = e['diagnosis'].index('>'); next unless p2
|
|
147
|
+
e['recipient'] = Sisimai::Address.s3s4(e[p1, p2 - p1])
|
|
142
148
|
end
|
|
143
149
|
|
|
144
|
-
return { 'ds' => dscontents, 'rfc822' =>
|
|
150
|
+
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
|
145
151
|
end
|
|
146
152
|
def description; return 'Sendmail version 5'; end
|
|
147
153
|
end
|