sisimai 4.22.3-java → 4.22.4-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sisimai might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/Benchmarks.mk +54 -0
- data/ChangeLog.md +23 -2
- data/Developers.mk +42 -35
- data/Makefile +10 -0
- data/README-JA.md +13 -13
- data/README.md +14 -14
- data/lib/sisimai.rb +12 -18
- data/lib/sisimai/address.rb +64 -82
- data/lib/sisimai/arf.rb +27 -42
- data/lib/sisimai/bite/email.rb +2 -4
- data/lib/sisimai/bite/email/activehunter.rb +12 -17
- data/lib/sisimai/bite/email/amazonses.rb +30 -48
- data/lib/sisimai/bite/email/amazonworkmail.rb +20 -27
- data/lib/sisimai/bite/email/aol.rb +27 -35
- data/lib/sisimai/bite/email/apachejames.rb +17 -28
- data/lib/sisimai/bite/email/bigfoot.rb +20 -33
- data/lib/sisimai/bite/email/biglobe.rb +15 -24
- data/lib/sisimai/bite/email/courier.rb +37 -61
- data/lib/sisimai/bite/email/domino.rb +19 -28
- data/lib/sisimai/bite/email/einsundeins.rb +20 -34
- data/lib/sisimai/bite/email/exchange2003.rb +25 -43
- data/lib/sisimai/bite/email/exchange2007.rb +15 -23
- data/lib/sisimai/bite/email/exim.rb +101 -120
- data/lib/sisimai/bite/email/ezweb.rb +28 -44
- data/lib/sisimai/bite/email/facebook.rb +26 -37
- data/lib/sisimai/bite/email/fml.rb +11 -20
- data/lib/sisimai/bite/email/gmx.rb +17 -27
- data/lib/sisimai/bite/email/google.rb +19 -29
- data/lib/sisimai/bite/email/gsuite.rb +39 -48
- data/lib/sisimai/bite/email/imailserver.rb +25 -39
- data/lib/sisimai/bite/email/interscanmss.rb +19 -26
- data/lib/sisimai/bite/email/kddi.rb +20 -33
- data/lib/sisimai/bite/email/mailfoundry.rb +14 -24
- data/lib/sisimai/bite/email/mailmarshalsmtp.rb +15 -24
- data/lib/sisimai/bite/email/mailru.rb +40 -59
- data/lib/sisimai/bite/email/mcafee.rb +21 -35
- data/lib/sisimai/bite/email/messagelabs.rb +23 -38
- data/lib/sisimai/bite/email/messagingserver.rb +15 -27
- data/lib/sisimai/bite/email/mfilter.rb +19 -28
- data/lib/sisimai/bite/email/mxlogic.rb +31 -49
- data/lib/sisimai/bite/email/notes.rb +16 -24
- data/lib/sisimai/bite/email/office365.rb +29 -38
- data/lib/sisimai/bite/email/opensmtpd.rb +50 -67
- data/lib/sisimai/bite/email/outlook.rb +24 -36
- data/lib/sisimai/bite/email/postfix.rb +33 -42
- data/lib/sisimai/bite/email/qmail.rb +44 -59
- data/lib/sisimai/bite/email/receivingses.rb +28 -36
- data/lib/sisimai/bite/email/sendgrid.rb +28 -37
- data/lib/sisimai/bite/email/sendmail.rb +35 -51
- data/lib/sisimai/bite/email/surfcontrol.rb +17 -25
- data/lib/sisimai/bite/email/userdefined.rb +17 -28
- data/lib/sisimai/bite/email/v5sendmail.rb +32 -41
- data/lib/sisimai/bite/email/verizon.rb +31 -56
- data/lib/sisimai/bite/email/x1.rb +11 -18
- data/lib/sisimai/bite/email/x2.rb +11 -23
- data/lib/sisimai/bite/email/x3.rb +10 -19
- data/lib/sisimai/bite/email/x4.rb +46 -65
- data/lib/sisimai/bite/email/x5.rb +26 -37
- data/lib/sisimai/bite/email/yahoo.rb +11 -19
- data/lib/sisimai/bite/email/yandex.rb +19 -30
- data/lib/sisimai/bite/email/zoho.rb +21 -30
- data/lib/sisimai/bite/json.rb +1 -2
- data/lib/sisimai/bite/json/amazonses.rb +20 -25
- data/lib/sisimai/bite/json/sendgrid.rb +1 -1
- data/lib/sisimai/data.rb +36 -55
- data/lib/sisimai/data/json.rb +3 -3
- data/lib/sisimai/data/yaml.rb +1 -1
- data/lib/sisimai/datetime.rb +5 -21
- data/lib/sisimai/mail.rb +4 -6
- data/lib/sisimai/mail/maildir.rb +1 -1
- data/lib/sisimai/mda.rb +41 -44
- data/lib/sisimai/message.rb +2 -3
- data/lib/sisimai/message/email.rb +42 -52
- data/lib/sisimai/message/json.rb +7 -7
- data/lib/sisimai/mime.rb +25 -23
- data/lib/sisimai/order/email.rb +2 -2
- data/lib/sisimai/order/json.rb +2 -7
- data/lib/sisimai/reason.rb +41 -46
- data/lib/sisimai/reason/blocked.rb +60 -71
- data/lib/sisimai/reason/contenterror.rb +4 -8
- data/lib/sisimai/reason/delivered.rb +1 -3
- data/lib/sisimai/reason/exceedlimit.rb +10 -20
- data/lib/sisimai/reason/expired.rb +5 -9
- data/lib/sisimai/reason/feedback.rb +1 -3
- data/lib/sisimai/reason/filtered.rb +19 -38
- data/lib/sisimai/reason/hasmoved.rb +5 -8
- data/lib/sisimai/reason/hostunknown.rb +11 -18
- data/lib/sisimai/reason/mailboxfull.rb +14 -24
- data/lib/sisimai/reason/mailererror.rb +3 -5
- data/lib/sisimai/reason/mesgtoobig.rb +15 -25
- data/lib/sisimai/reason/networkerror.rb +8 -10
- data/lib/sisimai/reason/norelaying.rb +9 -14
- data/lib/sisimai/reason/notaccept.rb +9 -21
- data/lib/sisimai/reason/onhold.rb +3 -8
- data/lib/sisimai/reason/policyviolation.rb +8 -10
- data/lib/sisimai/reason/rejected.rb +36 -49
- data/lib/sisimai/reason/securityerror.rb +11 -13
- data/lib/sisimai/reason/spamdetected.rb +23 -37
- data/lib/sisimai/reason/suspend.rb +9 -10
- data/lib/sisimai/reason/syntaxerror.rb +3 -4
- data/lib/sisimai/reason/systemerror.rb +7 -9
- data/lib/sisimai/reason/systemfull.rb +2 -4
- data/lib/sisimai/reason/toomanyconn.rb +17 -30
- data/lib/sisimai/reason/undefined.rb +1 -3
- data/lib/sisimai/reason/userunknown.rb +28 -38
- data/lib/sisimai/reason/vacation.rb +4 -6
- data/lib/sisimai/reason/virusdetected.rb +4 -6
- data/lib/sisimai/rfc2606.rb +1 -2
- data/lib/sisimai/rfc3464.rb +87 -101
- data/lib/sisimai/rfc3834.rb +29 -39
- data/lib/sisimai/rfc5322.rb +17 -24
- data/lib/sisimai/rhost.rb +10 -7
- data/lib/sisimai/rhost/exchangeonline.rb +124 -255
- data/lib/sisimai/rhost/franceptt.rb +2 -2
- data/lib/sisimai/rhost/godaddy.rb +12 -25
- data/lib/sisimai/rhost/googleapps.rb +82 -183
- data/lib/sisimai/smtp.rb +4 -4
- data/lib/sisimai/smtp/error.rb +8 -8
- data/lib/sisimai/smtp/reply.rb +1 -1
- data/lib/sisimai/smtp/status.rb +1 -0
- data/lib/sisimai/string.rb +5 -7
- data/lib/sisimai/version.rb +1 -1
- data/set-of-emails/README.md +1 -1
- data/set-of-emails/maildir/bsd/README.md +50 -50
- data/sisimai-java.gemspec +1 -1
- data/sisimai.gemspec +1 -1
- metadata +4 -4
- data/lib/sisimai/skeleton.rb +0 -43
@@ -8,29 +8,18 @@ module Sisimai::Bite::Email
|
|
8
8
|
# Based on Sisimai::Bite::Email::Exim
|
9
9
|
require 'sisimai/bite/email'
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
:'
|
14
|
-
|
15
|
-
|Warning:[ ]message[ ].+[ ]delayed[ ]+
|
16
|
-
|Delivery[ ]Status[ ]Notification
|
17
|
-
)
|
18
|
-
}x,
|
19
|
-
:'message-id' => %r/\A[<]mxl[~][0-9a-f]+/,
|
20
|
-
}.freeze
|
21
|
-
Re1 = {
|
22
|
-
:rfc822 => %r/\AIncluded is a copy of the message header:\z/,
|
23
|
-
:begin => %r/\AThis message was created automatically by mail delivery software[.]\z/,
|
24
|
-
:endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
|
11
|
+
Indicators = Sisimai::Bite::Email.INDICATORS
|
12
|
+
StartingOf = {
|
13
|
+
message: ['This message was created automatically by mail delivery software.'],
|
14
|
+
rfc822: ['Included is a copy of the message header:'],
|
25
15
|
}.freeze
|
26
|
-
|
16
|
+
|
17
|
+
ReCommands = [
|
27
18
|
%r/SMTP error from remote (?:mail server|mailer) after ([A-Za-z]{4})/,
|
28
19
|
%r/SMTP error from remote (?:mail server|mailer) after end of ([A-Za-z]{4})/,
|
29
20
|
].freeze
|
30
|
-
|
31
|
-
userunknown: %r
|
32
|
-
user[ ]not[ ]found
|
33
|
-
}x,
|
21
|
+
ReFailures = {
|
22
|
+
userunknown: %r/user not found/,
|
34
23
|
hostunknown: %r{(?>
|
35
24
|
all[ ](?:
|
36
25
|
host[ ]address[ ]lookups[ ]failed[ ]permanently
|
@@ -39,12 +28,8 @@ module Sisimai::Bite::Email
|
|
39
28
|
|Unrouteable[ ]address
|
40
29
|
)
|
41
30
|
}x,
|
42
|
-
mailboxfull: %r
|
43
|
-
|
44
|
-
|error:[ ]quota[ ]exceed
|
45
|
-
)
|
46
|
-
}x,
|
47
|
-
notaccept: %r{(?:
|
31
|
+
mailboxfull: %r/(?:mailbox is full:?|error: quota exceed)/,
|
32
|
+
notaccept: %r{(?:
|
48
33
|
an[ ]MX[ ]or[ ]SRV[ ]record[ ]indicated[ ]no[ ]SMTP[ ]service
|
49
34
|
|no[ ]host[ ]found[ ]for[ ]existing[ ]SMTP[ ]connection
|
50
35
|
)
|
@@ -55,11 +40,9 @@ module Sisimai::Bite::Email
|
|
55
40
|
|LMTP[ ]error[ ]after[ ]
|
56
41
|
)
|
57
42
|
}x,
|
58
|
-
contenterror: %r
|
59
|
-
Too[ ]many[ ]["]Received["][ ]headers
|
60
|
-
}x,
|
43
|
+
contenterror: %r/Too many ["]Received["] headers/,
|
61
44
|
}.freeze
|
62
|
-
|
45
|
+
ReDelaying = %r{(?:
|
63
46
|
retry[ ]timeout[ ]exceeded
|
64
47
|
|No[ ]action[ ]is[ ]required[ ]on[ ]your[ ]part
|
65
48
|
|retry[ ]time[ ]not[ ]reached[ ]for[ ]any[ ]host[ ]after[ ]a[ ]long[ ]failure[ ]period
|
@@ -68,7 +51,6 @@ module Sisimai::Bite::Email
|
|
68
51
|
|Message[ ].+[ ](?:has[ ]been[ ]frozen|was[ ]frozen[ ]on[ ]arrival[ ]by[ ])
|
69
52
|
)
|
70
53
|
}x
|
71
|
-
Indicators = Sisimai::Bite::Email.INDICATORS
|
72
54
|
|
73
55
|
def description; return 'McAfee SaaS'; end
|
74
56
|
def smtpagent; return Sisimai::Bite.smtpagent(self); end
|
@@ -76,7 +58,6 @@ module Sisimai::Bite::Email
|
|
76
58
|
# X-MXL-NoteHash: ffffffffffffffff-0000000000000000000000000000000000000000
|
77
59
|
# X-MXL-Hash: 4c9d4d411993da17-bbd4212b6c887f6c23bab7db4bd87ef5edc00758
|
78
60
|
def headerlist; return ['X-MXL-NoteHash', 'X-MXL-Hash', 'X-MX-Bounce']; end
|
79
|
-
def pattern; return Re0; end
|
80
61
|
|
81
62
|
# Parse bounce messages from MXLogic
|
82
63
|
# @param [Hash] mhead Message headers of a bounce email
|
@@ -93,12 +74,18 @@ module Sisimai::Bite::Email
|
|
93
74
|
return nil unless mhead
|
94
75
|
return nil unless mbody
|
95
76
|
|
77
|
+
# :'message-id' => %r/\A[<]mxl[~][0-9a-f]+/,
|
96
78
|
match = 0
|
97
79
|
match += 1 if mhead['x-mx-bounce']
|
98
80
|
match += 1 if mhead['x-mxl-hash']
|
99
81
|
match += 1 if mhead['x-mxl-notehash']
|
100
|
-
match += 1 if mhead['
|
101
|
-
match += 1 if mhead['
|
82
|
+
match += 1 if mhead['from'].start_with?('Mail Delivery System')
|
83
|
+
match += 1 if mhead['subject'] =~ %r{(?:
|
84
|
+
Mail[ ]delivery[ ]failed(:[ ]returning[ ]message[ ]to[ ]sender)?
|
85
|
+
|Warning:[ ]message[ ].+[ ]delayed[ ]+
|
86
|
+
|Delivery[ ]Status[ ]Notification
|
87
|
+
)
|
88
|
+
}x
|
102
89
|
return nil if match.zero?
|
103
90
|
|
104
91
|
dscontents = [Sisimai::Bite.DELIVERYSTATUS]
|
@@ -110,10 +97,10 @@ module Sisimai::Bite::Email
|
|
110
97
|
localhost0 = '' # (String) Local MTA
|
111
98
|
v = nil
|
112
99
|
|
113
|
-
hasdivided.
|
100
|
+
while e = hasdivided.shift do
|
114
101
|
if readcursor.zero?
|
115
102
|
# Beginning of the bounce message or delivery status part
|
116
|
-
if e
|
103
|
+
if e == StartingOf[:message][0]
|
117
104
|
readcursor |= Indicators[:deliverystatus]
|
118
105
|
next
|
119
106
|
end
|
@@ -121,7 +108,7 @@ module Sisimai::Bite::Email
|
|
121
108
|
|
122
109
|
if (readcursor & Indicators[:'message-rfc822']).zero?
|
123
110
|
# Beginning of the original message part
|
124
|
-
if e
|
111
|
+
if e == StartingOf[:rfc822][0]
|
125
112
|
readcursor |= Indicators[:'message-rfc822']
|
126
113
|
next
|
127
114
|
end
|
@@ -135,7 +122,6 @@ module Sisimai::Bite::Email
|
|
135
122
|
next
|
136
123
|
end
|
137
124
|
rfc822list << e
|
138
|
-
|
139
125
|
else
|
140
126
|
# Before "message/rfc822"
|
141
127
|
next if (readcursor & Indicators[:deliverystatus]).zero?
|
@@ -168,8 +154,7 @@ module Sisimai::Bite::Email
|
|
168
154
|
elsif dscontents.size == recipients
|
169
155
|
# Error message
|
170
156
|
next if e.empty?
|
171
|
-
v['diagnosis']
|
172
|
-
v['diagnosis'] += e + ' '
|
157
|
+
v['diagnosis'] << e + ' '
|
173
158
|
end
|
174
159
|
end
|
175
160
|
end
|
@@ -199,16 +184,14 @@ module Sisimai::Bite::Email
|
|
199
184
|
end
|
200
185
|
|
201
186
|
unless e['rhost']
|
202
|
-
|
203
|
-
|
204
|
-
e['rhost'] = Sisimai::RFC5322.received(mhead['received'][-1]).pop
|
205
|
-
end
|
187
|
+
# Get localhost and remote host name from Received header.
|
188
|
+
e['rhost'] = Sisimai::RFC5322.received(mhead['received'][-1]).pop if mhead['received'].size > 0
|
206
189
|
end
|
207
190
|
end
|
208
191
|
|
209
192
|
unless e['command']
|
210
193
|
# Get the SMTP command name for the session
|
211
|
-
|
194
|
+
ReCommands.each do |r|
|
212
195
|
# Verify each regular expression of SMTP commands
|
213
196
|
if cv = e['diagnosis'].match(r)
|
214
197
|
e['command'] = cv[1].upcase
|
@@ -221,22 +204,21 @@ module Sisimai::Bite::Email
|
|
221
204
|
# MAIL | Connected to 192.0.2.135 but sender was rejected.
|
222
205
|
e['reason'] = 'rejected'
|
223
206
|
|
224
|
-
elsif e['command']
|
207
|
+
elsif %w[HELO EHLO].index(e['command'])
|
225
208
|
# HELO | Connected to 192.0.2.135 but my name was rejected.
|
226
209
|
e['reason'] = 'blocked'
|
227
|
-
|
228
210
|
else
|
229
211
|
# Verify each regular expression of session errors
|
230
|
-
|
212
|
+
ReFailures.each_key do |r|
|
231
213
|
# Check each regular expression
|
232
|
-
next unless e['diagnosis'] =~
|
214
|
+
next unless e['diagnosis'] =~ ReFailures[r]
|
233
215
|
e['reason'] = r.to_s
|
234
216
|
break
|
235
217
|
end
|
236
218
|
|
237
219
|
unless e['reason']
|
238
220
|
# The reason "expired"
|
239
|
-
e['reason'] = 'expired' if e['diagnosis'] =~
|
221
|
+
e['reason'] = 'expired' if e['diagnosis'] =~ ReDelaying
|
240
222
|
end
|
241
223
|
end
|
242
224
|
end
|
@@ -6,15 +6,12 @@ module Sisimai::Bite::Email
|
|
6
6
|
# Imported from p5-Sisimail/lib/Sisimai/Bite/Email/Notes.pm
|
7
7
|
require 'sisimai/bite/email'
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
:begin => %r/\A[-]+[ ]+Failure Reasons[ ]+[-]+\z/,
|
14
|
-
:rfc822 => %r/^[-]+[ ]+Returned Message[ ]+[-]+$/,
|
15
|
-
:endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
|
9
|
+
Indicators = Sisimai::Bite::Email.INDICATORS
|
10
|
+
StartingOf = {
|
11
|
+
message: ['------- Failure Reasons '],
|
12
|
+
rfc822: ['------- Returned Message '],
|
16
13
|
}.freeze
|
17
|
-
|
14
|
+
ReFailures = {
|
18
15
|
userunknown: %r{(?:
|
19
16
|
User[ ]not[ ]listed[ ]in[ ]public[ ]Name[ ][&][ ]Address[ ]Book
|
20
17
|
|ディレクトリのリストにありません
|
@@ -22,12 +19,10 @@ module Sisimai::Bite::Email
|
|
22
19
|
}x,
|
23
20
|
networkerror: %r/Message has exceeded maximum hop count/,
|
24
21
|
}.freeze
|
25
|
-
Indicators = Sisimai::Bite::Email.INDICATORS
|
26
22
|
|
27
23
|
def description; return 'Lotus Notes'; end
|
28
24
|
def smtpagent; return Sisimai::Bite.smtpagent(self); end
|
29
25
|
def headerlist; return []; end
|
30
|
-
def pattern; return Re0; end
|
31
26
|
|
32
27
|
# Parse bounce messages from Lotus Notes
|
33
28
|
# @param [Hash] mhead Message headers of a bounce email
|
@@ -43,7 +38,7 @@ module Sisimai::Bite::Email
|
|
43
38
|
def scan(mhead, mbody)
|
44
39
|
return nil unless mhead
|
45
40
|
return nil unless mbody
|
46
|
-
return nil unless mhead['subject']
|
41
|
+
return nil unless mhead['subject'].start_with?('Undeliverable message')
|
47
42
|
|
48
43
|
dscontents = [Sisimai::Bite.DELIVERYSTATUS]
|
49
44
|
hasdivided = mbody.split("\n")
|
@@ -56,10 +51,10 @@ module Sisimai::Bite::Email
|
|
56
51
|
encodedmsg = ''
|
57
52
|
v = nil
|
58
53
|
|
59
|
-
hasdivided.
|
54
|
+
while e = hasdivided.shift do
|
60
55
|
if readcursor.zero?
|
61
56
|
# Beginning of the bounce message or delivery status part
|
62
|
-
if e
|
57
|
+
if e.start_with?(StartingOf[:message][0])
|
63
58
|
readcursor |= Indicators[:deliverystatus]
|
64
59
|
next
|
65
60
|
end
|
@@ -67,7 +62,7 @@ module Sisimai::Bite::Email
|
|
67
62
|
|
68
63
|
if (readcursor & Indicators[:'message-rfc822']).zero?
|
69
64
|
# Beginning of the original message part
|
70
|
-
if e
|
65
|
+
if e.start_with?(StartingOf[:rfc822][0])
|
71
66
|
readcursor |= Indicators[:'message-rfc822']
|
72
67
|
next
|
73
68
|
end
|
@@ -89,7 +84,6 @@ module Sisimai::Bite::Email
|
|
89
84
|
next
|
90
85
|
end
|
91
86
|
rfc822list << e
|
92
|
-
|
93
87
|
else
|
94
88
|
# Before "message/rfc822"
|
95
89
|
next if (readcursor & Indicators[:deliverystatus]).zero?
|
@@ -110,10 +104,9 @@ module Sisimai::Bite::Email
|
|
110
104
|
end
|
111
105
|
v['recipient'] ||= e
|
112
106
|
recipients += 1
|
113
|
-
|
114
107
|
else
|
115
|
-
next if e
|
116
|
-
next if e
|
108
|
+
next if e.empty?
|
109
|
+
next if e.start_with?('-')
|
117
110
|
|
118
111
|
if e =~ /[^\x20-\x7e]/
|
119
112
|
# Error message is not ISO-8859-1
|
@@ -130,11 +123,11 @@ module Sisimai::Bite::Email
|
|
130
123
|
encodedmsg = removedmsg
|
131
124
|
end
|
132
125
|
v['diagnosis'] ||= ''
|
133
|
-
v['diagnosis']
|
126
|
+
v['diagnosis'] << encodedmsg
|
134
127
|
else
|
135
128
|
# Error message does not include multi-byte character
|
136
129
|
v['diagnosis'] ||= ''
|
137
|
-
v['diagnosis']
|
130
|
+
v['diagnosis'] << e
|
138
131
|
end
|
139
132
|
end
|
140
133
|
end
|
@@ -150,19 +143,18 @@ module Sisimai::Bite::Email
|
|
150
143
|
break
|
151
144
|
end
|
152
145
|
end
|
153
|
-
|
154
146
|
return nil if recipients.zero?
|
147
|
+
|
155
148
|
require 'sisimai/string'
|
156
149
|
require 'sisimai/smtp/status'
|
157
|
-
|
158
150
|
dscontents.map do |e|
|
159
151
|
e['agent'] = self.smtpagent
|
160
152
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
161
153
|
e['recipient'] = Sisimai::Address.s3s4(e['recipient'])
|
162
154
|
|
163
|
-
|
155
|
+
ReFailures.each_key do |r|
|
164
156
|
# Check each regular expression of Notes error messages
|
165
|
-
next unless e['diagnosis'] =~
|
157
|
+
next unless e['diagnosis'] =~ ReFailures[r]
|
166
158
|
e['reason'] = r.to_s
|
167
159
|
pseudostatus = Sisimai::SMTP::Status.code(r.to_s)
|
168
160
|
e['status'] = pseudostatus if pseudostatus.size > 0
|
@@ -7,23 +7,20 @@ module Sisimai::Bite::Email
|
|
7
7
|
# Imported from p5-Sisimail/lib/Sisimai/Bite/Email/Office365.pm
|
8
8
|
require 'sisimai/bite/email'
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
:'
|
13
|
-
:'
|
10
|
+
Indicators = Sisimai::Bite::Email.INDICATORS
|
11
|
+
StartingOf = {
|
12
|
+
rfc822: ['Content-Type: message/rfc822'],
|
13
|
+
error: ['Diagnostic information for administrators:'],
|
14
|
+
eoerr: ['Original message headers:'],
|
14
15
|
}.freeze
|
15
|
-
|
16
|
-
:
|
16
|
+
MarkingsOf = {
|
17
|
+
message: %r{\A(?:
|
17
18
|
Delivery[ ]has[ ]failed[ ]to[ ]these[ ]recipients[ ]or[ ]groups:
|
18
19
|
|.+[ ]rejected[ ]your[ ]message[ ]to[ ]the[ ]following[ ]e[-]?mail[ ]addresses:
|
19
20
|
)
|
20
21
|
}x,
|
21
|
-
:error => %r/\ADiagnostic information for administrators:\z/,
|
22
|
-
:eoerr => %r/\AOriginal message headers:\z/,
|
23
|
-
:rfc822 => %r|\AContent-Type: message/rfc822\z|,
|
24
|
-
:endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
|
25
22
|
}.freeze
|
26
|
-
|
23
|
+
StatusList = {
|
27
24
|
# https://support.office.com/en-us/article/Email-non-delivery-reports-in-Office-365-51daa6b9-2e35-49c4-a0c9-df85bf8533c3
|
28
25
|
%r/\A4[.]4[.]7\z/ => 'expired',
|
29
26
|
%r/\A4[.]7[.]26\z/ => 'securityerror',
|
@@ -49,7 +46,6 @@ module Sisimai::Bite::Email
|
|
49
46
|
%r/\A5[.]7[.]6[1-4]\d\z/ => 'blocked',
|
50
47
|
%r/\A5[.]7[.]7[0-4]\d\z/ => 'toomanyconn',
|
51
48
|
}.freeze
|
52
|
-
Indicators = Sisimai::Bite::Email.INDICATORS
|
53
49
|
|
54
50
|
def description; return 'Microsoft Office 365: http://office.microsoft.com/'; end
|
55
51
|
def smtpagent; return Sisimai::Bite.smtpagent(self); end
|
@@ -72,7 +68,6 @@ module Sisimai::Bite::Email
|
|
72
68
|
'X-MS-Exchange-Transport-CrossTenantHeadersStamped',
|
73
69
|
]
|
74
70
|
end
|
75
|
-
def pattern; return Re0; end
|
76
71
|
|
77
72
|
# Parse bounce messages from Microsoft Office 365
|
78
73
|
# @param [Hash] mhead Message headers of a bounce email
|
@@ -89,8 +84,9 @@ module Sisimai::Bite::Email
|
|
89
84
|
return nil unless mhead
|
90
85
|
return nil unless mbody
|
91
86
|
|
87
|
+
tryto = %r/.+[.](?:outbound[.]protection|prod)[.]outlook[.]com\b/
|
92
88
|
match = 0
|
93
|
-
match += 1 if mhead['subject']
|
89
|
+
match += 1 if mhead['subject'].include?('Undeliverable:')
|
94
90
|
match += 1 if mhead['x-ms-exchange-message-is-ndr']
|
95
91
|
match += 1 if mhead['x-microsoft-antispam-prvs']
|
96
92
|
match += 1 if mhead['x-exchange-antispam-report-test']
|
@@ -98,10 +94,10 @@ module Sisimai::Bite::Email
|
|
98
94
|
match += 1 if mhead['x-ms-exchange-crosstenant-originalarrivaltime']
|
99
95
|
match += 1 if mhead['x-ms-exchange-crosstenant-fromentityheader']
|
100
96
|
match += 1 if mhead['x-ms-exchange-transport-crosstenantheadersstamped']
|
101
|
-
match += 1 if mhead['received'].find { |a| a =~
|
97
|
+
match += 1 if mhead['received'].find { |a| a =~ tryto }
|
102
98
|
if mhead['message-id']
|
103
99
|
# Message-ID: <00000000-0000-0000-0000-000000000000@*.*.prod.outlook.com>
|
104
|
-
match += 1 if mhead['message-id'] =~
|
100
|
+
match += 1 if mhead['message-id'] =~ tryto
|
105
101
|
end
|
106
102
|
return nil if match < 2
|
107
103
|
|
@@ -116,10 +112,10 @@ module Sisimai::Bite::Email
|
|
116
112
|
htmlbegins = false # (Boolean) Flag for HTML part
|
117
113
|
v = nil
|
118
114
|
|
119
|
-
hasdivided.
|
115
|
+
while e = hasdivided.shift do
|
120
116
|
if readcursor.zero?
|
121
117
|
# Beginning of the bounce message or delivery status part
|
122
|
-
if e =~
|
118
|
+
if e =~ MarkingsOf[:message]
|
123
119
|
readcursor |= Indicators[:deliverystatus]
|
124
120
|
next
|
125
121
|
end
|
@@ -127,7 +123,7 @@ module Sisimai::Bite::Email
|
|
127
123
|
|
128
124
|
if (readcursor & Indicators[:'message-rfc822']).zero?
|
129
125
|
# Beginning of the original message part
|
130
|
-
if e
|
126
|
+
if e == StartingOf[:rfc822][0]
|
131
127
|
readcursor |= Indicators[:'message-rfc822']
|
132
128
|
next
|
133
129
|
end
|
@@ -141,7 +137,6 @@ module Sisimai::Bite::Email
|
|
141
137
|
next
|
142
138
|
end
|
143
139
|
rfc822list << e
|
144
|
-
|
145
140
|
else
|
146
141
|
# Before "message/rfc822"
|
147
142
|
next if (readcursor & Indicators[:deliverystatus]).zero?
|
@@ -166,43 +161,40 @@ module Sisimai::Bite::Email
|
|
166
161
|
elsif cv = e.match(/\AGenerating server: (.+)\z/)
|
167
162
|
# Generating server: FFFFFFFFFFFF.e0.prod.outlook.com
|
168
163
|
connheader['lhost'] = cv[1].downcase
|
169
|
-
|
170
164
|
else
|
171
165
|
if endoferror
|
172
166
|
# After "Original message headers:"
|
173
167
|
if htmlbegins
|
174
168
|
# <html> .. </html>
|
175
|
-
htmlbegins = false if e
|
169
|
+
htmlbegins = false if e.start_with?('</html>')
|
176
170
|
next
|
177
171
|
end
|
178
172
|
|
179
|
-
if cv = e.match(/\
|
173
|
+
if cv = e.match(/\AAction:[ ]*(.+)\z/)
|
180
174
|
# Action: failed
|
181
175
|
v['action'] = cv[1].downcase
|
182
176
|
|
183
|
-
elsif cv = e.match(/\
|
177
|
+
elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
|
184
178
|
# Status:5.2.0
|
185
179
|
v['status'] = cv[1]
|
186
180
|
|
187
|
-
elsif cv = e.match(/\
|
181
|
+
elsif cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
|
188
182
|
# Reporting-MTA: dns;BLU004-OMC3S13.hotmail.example.com
|
189
183
|
connheader['lhost'] = cv[1].downcase
|
190
184
|
|
191
|
-
elsif cv = e.match(/\
|
185
|
+
elsif cv = e.match(/\AReceived-From-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
|
192
186
|
# Reporting-MTA: dns;BLU004-OMC3S13.hotmail.example.com
|
193
187
|
connheader['rhost'] = cv[1].downcase
|
194
188
|
|
195
|
-
elsif cv = e.match(/\
|
189
|
+
elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
|
196
190
|
# Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
|
197
191
|
next if connheader['date']
|
198
192
|
connheader['date'] = cv[1]
|
199
|
-
|
200
193
|
else
|
201
|
-
htmlbegins = true if e
|
194
|
+
htmlbegins = true if e.start_with?('<html>')
|
202
195
|
end
|
203
|
-
|
204
196
|
else
|
205
|
-
if e
|
197
|
+
if e == StartingOf[:error][0]
|
206
198
|
# Diagnostic information for administrators:
|
207
199
|
v['diagnosis'] = e
|
208
200
|
else
|
@@ -210,23 +202,22 @@ module Sisimai::Bite::Email
|
|
210
202
|
# Remote Server returned '550 5.1.10 RESOLVER.ADR.RecipientNotFound; Recipien=
|
211
203
|
# t not found by SMTP address lookup'
|
212
204
|
next unless v['diagnosis']
|
213
|
-
if e
|
205
|
+
if e == StartingOf[:eoerr][0]
|
214
206
|
# Original message headers:
|
215
207
|
endoferror = true
|
216
208
|
next
|
217
209
|
end
|
218
|
-
v['diagnosis']
|
210
|
+
v['diagnosis'] << ' ' << e
|
219
211
|
end
|
220
212
|
end
|
221
213
|
end
|
222
214
|
|
223
215
|
end
|
224
216
|
end
|
225
|
-
|
226
217
|
return nil if recipients.zero?
|
218
|
+
|
227
219
|
require 'sisimai/string'
|
228
220
|
require 'sisimai/smtp/status'
|
229
|
-
|
230
221
|
dscontents.map do |e|
|
231
222
|
# Set default values if each value is empty.
|
232
223
|
connheader.each_key { |a| e[a] ||= connheader[a] || '' }
|
@@ -235,17 +226,17 @@ module Sisimai::Bite::Email
|
|
235
226
|
e['status'] ||= ''
|
236
227
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
|
237
228
|
|
238
|
-
if e['status'].empty? || e['status']
|
229
|
+
if e['status'].empty? || e['status'].end_with?('.0.0')
|
239
230
|
# There is no value of Status header or the value is 5.0.0, 4.0.0
|
240
231
|
pseudostatus = Sisimai::SMTP::Status.find(e['diagnosis'])
|
241
232
|
e['status'] = pseudostatus if pseudostatus.size > 0
|
242
233
|
end
|
243
234
|
next unless e['status']
|
244
235
|
|
245
|
-
|
236
|
+
StatusList.each_key do |f|
|
246
237
|
# Try to match with each key as a regular expression
|
247
238
|
next unless e['status'] =~ f
|
248
|
-
e['reason'] =
|
239
|
+
e['reason'] = StatusList[f]
|
249
240
|
break
|
250
241
|
end
|
251
242
|
end
|