sisimai 4.25.4 → 4.25.5
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 +2 -2
- data/ChangeLog.md +50 -0
- data/README-JA.md +10 -37
- data/README.md +10 -37
- data/lib/sisimai.rb +15 -64
- data/lib/sisimai/address.rb +13 -17
- data/lib/sisimai/arf.rb +4 -4
- data/lib/sisimai/data.rb +3 -5
- data/lib/sisimai/lhost.rb +0 -14
- data/lib/sisimai/lhost/activehunter.rb +31 -55
- data/lib/sisimai/lhost/amavis.rb +41 -66
- data/lib/sisimai/lhost/amazonses.rb +189 -235
- data/lib/sisimai/lhost/amazonworkmail.rb +48 -71
- data/lib/sisimai/lhost/aol.rb +49 -75
- data/lib/sisimai/lhost/apachejames.rb +60 -83
- data/lib/sisimai/lhost/bigfoot.rb +61 -85
- data/lib/sisimai/lhost/biglobe.rb +40 -62
- data/lib/sisimai/lhost/courier.rb +60 -82
- data/lib/sisimai/lhost/domino.rb +50 -76
- data/lib/sisimai/lhost/einsundeins.rb +57 -55
- data/lib/sisimai/lhost/exchange2003.rb +79 -101
- data/lib/sisimai/lhost/exchange2007.rb +66 -74
- data/lib/sisimai/lhost/exim.rb +119 -142
- data/lib/sisimai/lhost/ezweb.rb +53 -73
- data/lib/sisimai/lhost/facebook.rb +49 -75
- data/lib/sisimai/lhost/fml.rb +25 -50
- data/lib/sisimai/lhost/gmx.rb +55 -79
- data/lib/sisimai/lhost/google.rb +39 -66
- data/lib/sisimai/lhost/gsuite.rb +74 -94
- data/lib/sisimai/lhost/imailserver.rb +34 -67
- data/lib/sisimai/lhost/interscanmss.rb +33 -67
- data/lib/sisimai/lhost/kddi.rb +30 -52
- data/lib/sisimai/lhost/mailfoundry.rb +35 -57
- data/lib/sisimai/lhost/mailmarshalsmtp.rb +66 -89
- data/lib/sisimai/lhost/mailru.rb +51 -76
- data/lib/sisimai/lhost/mcafee.rb +53 -79
- data/lib/sisimai/lhost/messagelabs.rb +49 -75
- data/lib/sisimai/lhost/messagingserver.rb +91 -113
- data/lib/sisimai/lhost/mfilter.rb +50 -70
- data/lib/sisimai/lhost/mxlogic.rb +38 -63
- data/lib/sisimai/lhost/notes.rb +53 -82
- data/lib/sisimai/lhost/office365.rb +64 -81
- data/lib/sisimai/lhost/opensmtpd.rb +30 -52
- data/lib/sisimai/lhost/outlook.rb +49 -75
- data/lib/sisimai/lhost/postfix.rb +116 -117
- data/lib/sisimai/lhost/qmail.rb +33 -55
- data/lib/sisimai/lhost/receivingses.rb +49 -75
- data/lib/sisimai/lhost/sendgrid.rb +68 -203
- data/lib/sisimai/lhost/sendmail.rb +101 -125
- data/lib/sisimai/lhost/surfcontrol.rb +53 -79
- data/lib/sisimai/lhost/userdefined.rb +15 -35
- data/lib/sisimai/lhost/v5sendmail.rb +59 -89
- data/lib/sisimai/lhost/verizon.rb +75 -124
- data/lib/sisimai/lhost/x1.rb +30 -54
- data/lib/sisimai/lhost/x2.rb +28 -52
- data/lib/sisimai/lhost/x3.rb +44 -68
- data/lib/sisimai/lhost/x4.rb +34 -58
- data/lib/sisimai/lhost/x5.rb +42 -70
- data/lib/sisimai/lhost/yahoo.rb +44 -68
- data/lib/sisimai/lhost/yandex.rb +59 -85
- data/lib/sisimai/lhost/zoho.rb +54 -78
- data/lib/sisimai/mail.rb +5 -9
- data/lib/sisimai/mail/maildir.rb +10 -14
- data/lib/sisimai/mail/mbox.rb +8 -12
- data/lib/sisimai/mail/memory.rb +5 -9
- data/lib/sisimai/mail/stdin.rb +7 -11
- data/lib/sisimai/mda.rb +2 -2
- data/lib/sisimai/message.rb +51 -154
- data/lib/sisimai/mime.rb +2 -2
- data/lib/sisimai/order.rb +2 -27
- data/lib/sisimai/reason.rb +3 -5
- data/lib/sisimai/rfc1894.rb +1 -1
- data/lib/sisimai/rfc3464.rb +29 -29
- data/lib/sisimai/rfc3834.rb +7 -6
- data/lib/sisimai/rfc5322.rb +20 -31
- data/lib/sisimai/rhost/franceptt.rb +120 -24
- data/lib/sisimai/rhost/iua.rb +1 -1
- data/lib/sisimai/smtp/error.rb +7 -7
- data/lib/sisimai/version.rb +1 -1
- data/set-of-emails/maildir/bsd/email-einsundeins-03.eml +66 -0
- data/set-of-emails/maildir/bsd/email-exchange2007-05.eml +1469 -0
- data/set-of-emails/maildir/bsd/email-exchange2007-06.eml +764 -0
- data/set-of-emails/maildir/bsd/email-postfix-64.eml +96 -0
- data/set-of-emails/maildir/bsd/rfc3834-03.eml +26 -0
- data/set-of-emails/maildir/bsd/rhost-franceptt-04.eml +66 -0
- data/set-of-emails/maildir/bsd/rhost-franceptt-05.eml +96 -0
- data/set-of-emails/maildir/bsd/rhost-franceptt-06.eml +100 -0
- data/set-of-emails/maildir/bsd/rhost-franceptt-07.eml +97 -0
- data/set-of-emails/maildir/bsd/rhost-franceptt-08.eml +78 -0
- data/set-of-emails/maildir/bsd/rhost-franceptt-10.eml +79 -0
- data/set-of-emails/maildir/bsd/rhost-franceptt-11.eml +96 -0
- metadata +14 -9
- data/lib/sisimai/bite.rb +0 -42
- data/lib/sisimai/bite/email.rb +0 -18
- data/lib/sisimai/bite/json.rb +0 -16
- data/lib/sisimai/message/email.rb +0 -26
- data/lib/sisimai/message/json.rb +0 -24
- data/lib/sisimai/order/email.rb +0 -20
- data/lib/sisimai/order/json.rb +0 -16
data/lib/sisimai/data.rb
CHANGED
@@ -35,7 +35,7 @@ module Sisimai
|
|
35
35
|
:deliverystatus, # [String] Delivery Status(DSN)
|
36
36
|
:timezoneoffset, # [Integer] Time zone offset(seconds)
|
37
37
|
]
|
38
|
-
|
38
|
+
attr_accessor(*@@rwaccessors)
|
39
39
|
|
40
40
|
EndOfEmail = Sisimai::String.EOM
|
41
41
|
RetryIndex = Sisimai::Reason.retry
|
@@ -153,7 +153,6 @@ module Sisimai
|
|
153
153
|
# Detect email address from message/rfc822 part
|
154
154
|
fieldorder[:addresser].each do |f|
|
155
155
|
# Check each header in message/rfc822 part
|
156
|
-
next unless rfc822data.key?(f)
|
157
156
|
next unless rfc822data[f]
|
158
157
|
next if rfc822data[f].empty?
|
159
158
|
|
@@ -312,7 +311,7 @@ module Sisimai
|
|
312
311
|
o = Sisimai::Data.new(p)
|
313
312
|
next unless o.recipient
|
314
313
|
|
315
|
-
if o.reason.empty? || RetryIndex
|
314
|
+
if o.reason.empty? || RetryIndex[o.reason]
|
316
315
|
# Decide the reason of email bounce
|
317
316
|
r = ''
|
318
317
|
r = Sisimai::Rhost.get(o) if Sisimai::Rhost.match(o.rhost) # Remote host dependent error
|
@@ -348,9 +347,8 @@ module Sisimai
|
|
348
347
|
textasargv = (o.replycode + ' ' + p['diagnosticcode']).lstrip
|
349
348
|
getchecked = Sisimai::SMTP::Error.is_permanent(textasargv)
|
350
349
|
tmpfailure = getchecked.nil? ? false : (getchecked ? false : true)
|
351
|
-
pseudocode = Sisimai::SMTP::Status.code(o.reason, tmpfailure)
|
352
350
|
|
353
|
-
if pseudocode
|
351
|
+
if pseudocode = Sisimai::SMTP::Status.code(o.reason, tmpfailure)
|
354
352
|
# Set the value of "deliverystatus" and "softbounce"
|
355
353
|
o.deliverystatus = pseudocode
|
356
354
|
|
data/lib/sisimai/lhost.rb
CHANGED
@@ -81,20 +81,6 @@ module Sisimai
|
|
81
81
|
# part or nil if it failed to parse or
|
82
82
|
# the arguments are missing
|
83
83
|
def make; return nil; end
|
84
|
-
|
85
|
-
# @abstract Print warnings about an obsoleted method. This method will be
|
86
|
-
# removed at the future release of Sisimai
|
87
|
-
# @until v4.25.5
|
88
|
-
def warn(whois = '', useit = nil)
|
89
|
-
label = ' ***warning:'
|
90
|
-
methodname = caller[0][/`.*'/][1..-2]
|
91
|
-
messageset = sprintf("%s %s.%s is marked as obsoleted", label, whois, methodname)
|
92
|
-
|
93
|
-
useit ||= methodname
|
94
|
-
messageset << sprintf(" and will be removed at %s.", removedat)
|
95
|
-
messageset << sprintf(" Use %s.%s instead.\n", self.name, useit) if useit != 'gone'
|
96
|
-
Kernel.warn messageset
|
97
|
-
end
|
98
84
|
end
|
99
85
|
end
|
100
86
|
end
|
@@ -8,10 +8,8 @@ module Sisimai::Lhost
|
|
8
8
|
require 'sisimai/lhost'
|
9
9
|
|
10
10
|
Indicators = Sisimai::Lhost.INDICATORS
|
11
|
-
|
12
|
-
|
13
|
-
rfc822: ['Content-type: message/rfc822'],
|
14
|
-
}.freeze
|
11
|
+
ReBackbone = %r|^Content-type:[ ]message/rfc822|.freeze
|
12
|
+
StartingOf = { message: [' ----- The following addresses had permanent fatal errors -----'] }.freeze
|
15
13
|
|
16
14
|
def description; return 'TransWARE Active!hunter'; end
|
17
15
|
def smtpagent; return Sisimai::Lhost.smtpagent(self); end
|
@@ -34,68 +32,47 @@ module Sisimai::Lhost
|
|
34
32
|
return nil unless mhead['x-ahmailid']
|
35
33
|
|
36
34
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
37
|
-
|
38
|
-
|
39
|
-
blanklines = 0 # (Integer) The number of blank lines
|
35
|
+
emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
|
36
|
+
bodyslices = emailsteak[0].split("\n")
|
40
37
|
readcursor = 0 # (Integer) Points the current cursor position
|
41
38
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
42
39
|
v = nil
|
43
40
|
|
44
|
-
while e =
|
41
|
+
while e = bodyslices.shift do
|
42
|
+
# Read error messages and delivery status lines from the head of the email
|
43
|
+
# to the previous line of the beginning of the original message.
|
45
44
|
if readcursor == 0
|
46
45
|
# Beginning of the bounce message or delivery status part
|
47
|
-
if e == StartingOf[:message][0]
|
48
|
-
|
49
|
-
next
|
50
|
-
end
|
46
|
+
readcursor |= Indicators[:deliverystatus] if e == StartingOf[:message][0]
|
47
|
+
next
|
51
48
|
end
|
49
|
+
next if (readcursor & Indicators[:deliverystatus]) == 0
|
50
|
+
next if e.empty?
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
52
|
+
# ----- The following addresses had permanent fatal errors -----
|
53
|
+
#
|
54
|
+
# >>> kijitora@example.org <kijitora@example.org>
|
55
|
+
#
|
56
|
+
# ----- Transcript of session follows -----
|
57
|
+
# 550 sorry, no mailbox here by that name (#5.1.1 - chkusr)
|
58
|
+
v = dscontents[-1]
|
60
59
|
|
61
|
-
if
|
62
|
-
#
|
63
|
-
if
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
if cv = e.match(/\A[>]{3}[ \t]+.+[<]([^ ]+?[@][^ ]+?)[>]\z/)
|
61
|
+
# >>> kijitora@example.org <kijitora@example.org>
|
62
|
+
if v['recipient']
|
63
|
+
# There are multiple recipient addresses in the message body.
|
64
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
65
|
+
v = dscontents[-1]
|
67
66
|
end
|
68
|
-
|
67
|
+
v['recipient'] = cv[1]
|
68
|
+
v['diagnosis'] = ''
|
69
|
+
recipients += 1
|
69
70
|
else
|
70
|
-
# Error message part
|
71
|
-
next if (readcursor & Indicators[:deliverystatus]) == 0
|
72
|
-
next if e.empty?
|
73
|
-
|
74
|
-
# ----- The following addresses had permanent fatal errors -----
|
75
|
-
#
|
76
|
-
# >>> kijitora@example.org <kijitora@example.org>
|
77
|
-
#
|
78
71
|
# ----- Transcript of session follows -----
|
79
72
|
# 550 sorry, no mailbox here by that name (#5.1.1 - chkusr)
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
# >>> kijitora@example.org <kijitora@example.org>
|
84
|
-
if v['recipient']
|
85
|
-
# There are multiple recipient addresses in the message body.
|
86
|
-
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
87
|
-
v = dscontents[-1]
|
88
|
-
end
|
89
|
-
v['recipient'] = cv[1]
|
90
|
-
v['diagnosis'] = ''
|
91
|
-
recipients += 1
|
92
|
-
else
|
93
|
-
# ----- Transcript of session follows -----
|
94
|
-
# 550 sorry, no mailbox here by that name (#5.1.1 - chkusr)
|
95
|
-
next unless e =~ /\A[0-9A-Za-z]+/
|
96
|
-
next unless v['diagnosis'].empty?
|
97
|
-
v['diagnosis'] = e
|
98
|
-
end
|
73
|
+
next unless e =~ /\A[0-9A-Za-z]+/
|
74
|
+
next unless v['diagnosis'].empty?
|
75
|
+
v['diagnosis'] = e
|
99
76
|
end
|
100
77
|
end
|
101
78
|
return nil unless recipients > 0
|
@@ -107,8 +84,7 @@ module Sisimai::Lhost
|
|
107
84
|
e.each_key { |a| e[a] ||= '' }
|
108
85
|
end
|
109
86
|
|
110
|
-
|
111
|
-
return { 'ds' => dscontents, 'rfc822' => rfc822part }
|
87
|
+
return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
|
112
88
|
end
|
113
89
|
|
114
90
|
end
|
data/lib/sisimai/lhost/amavis.rb
CHANGED
@@ -7,6 +7,7 @@ module Sisimai::Lhost
|
|
7
7
|
require 'sisimai/lhost'
|
8
8
|
|
9
9
|
Indicators = Sisimai::Lhost.INDICATORS
|
10
|
+
ReBackbone = %r|^Content-Type:[ ]text/rfc822-headers|.freeze
|
10
11
|
StartingOf = {
|
11
12
|
message: ['The message '],
|
12
13
|
rfc822: ['Content-Type: text/rfc822-headers'],
|
@@ -36,82 +37,57 @@ module Sisimai::Lhost
|
|
36
37
|
permessage = {} # (Hash) Store values of each Per-Message field
|
37
38
|
|
38
39
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
39
|
-
|
40
|
-
|
41
|
-
rfc822list = [] # (Array) Each line in message/rfc822 part string
|
42
|
-
blanklines = 0 # (Integer) The number of blank lines
|
40
|
+
emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
|
41
|
+
bodyslices = emailsteak[0].split("\n")
|
43
42
|
readcursor = 0 # (Integer) Points the current cursor position
|
44
43
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
45
44
|
v = nil
|
46
45
|
|
47
|
-
while e =
|
48
|
-
#
|
49
|
-
|
50
|
-
p = havepassed[-2]
|
46
|
+
while e = bodyslices.shift do
|
47
|
+
# Read error messages and delivery status lines from the head of the email
|
48
|
+
# to the previous line of the beginning of the original message.
|
51
49
|
|
52
50
|
if readcursor == 0
|
53
51
|
# Beginning of the bounce message or message/delivery-status part
|
54
|
-
if e.start_with?(StartingOf[:message][0])
|
55
|
-
|
56
|
-
next
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
if (readcursor & Indicators[:'message-rfc822']) == 0
|
61
|
-
# Beginning of the original message part(message/rfc822)
|
62
|
-
if e == StartingOf[:rfc822][0]
|
63
|
-
readcursor |= Indicators[:'message-rfc822']
|
64
|
-
next
|
65
|
-
end
|
52
|
+
readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
|
53
|
+
next
|
66
54
|
end
|
67
|
-
|
68
|
-
if
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
#
|
78
|
-
|
79
|
-
next if e.empty?
|
80
|
-
next unless f = Sisimai::RFC1894.match(e)
|
81
|
-
|
82
|
-
# "e" matched with any field defined in RFC3464
|
83
|
-
next unless o = Sisimai::RFC1894.field(e)
|
84
|
-
v = dscontents[-1]
|
85
|
-
|
86
|
-
if o[-1] == 'addr'
|
55
|
+
next if (readcursor & Indicators[:deliverystatus]) == 0
|
56
|
+
next if e.empty?
|
57
|
+
next unless f = Sisimai::RFC1894.match(e)
|
58
|
+
|
59
|
+
# "e" matched with any field defined in RFC3464
|
60
|
+
next unless o = Sisimai::RFC1894.field(e)
|
61
|
+
v = dscontents[-1]
|
62
|
+
|
63
|
+
if o[-1] == 'addr'
|
64
|
+
# Final-Recipient: rfc822; kijitora@example.jp
|
65
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
66
|
+
if o[0] == 'final-recipient'
|
87
67
|
# Final-Recipient: rfc822; kijitora@example.jp
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
# There are multiple recipient addresses in the message body.
|
93
|
-
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
94
|
-
v = dscontents[-1]
|
95
|
-
end
|
96
|
-
v['recipient'] = o[2]
|
97
|
-
recipients += 1
|
98
|
-
else
|
99
|
-
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
100
|
-
v['alias'] = o[2]
|
68
|
+
if v['recipient']
|
69
|
+
# There are multiple recipient addresses in the message body.
|
70
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
71
|
+
v = dscontents[-1]
|
101
72
|
end
|
102
|
-
|
103
|
-
|
104
|
-
v['spec'] = o[1]
|
105
|
-
v['diagnosis'] = o[2]
|
73
|
+
v['recipient'] = o[2]
|
74
|
+
recipients += 1
|
106
75
|
else
|
107
|
-
#
|
108
|
-
|
109
|
-
v[fieldtable[o[0]]] = o[2]
|
110
|
-
|
111
|
-
next unless f == 1
|
112
|
-
permessage[fieldtable[o[0]]] = o[2]
|
76
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
77
|
+
v['alias'] = o[2]
|
113
78
|
end
|
114
|
-
|
79
|
+
elsif o[-1] == 'code'
|
80
|
+
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
81
|
+
v['spec'] = o[1]
|
82
|
+
v['diagnosis'] = o[2]
|
83
|
+
else
|
84
|
+
# Other DSN fields defined in RFC3464
|
85
|
+
next unless fieldtable[o[0]]
|
86
|
+
v[fieldtable[o[0]]] = o[2]
|
87
|
+
|
88
|
+
next unless f == 1
|
89
|
+
permessage[fieldtable[o[0]]] = o[2]
|
90
|
+
end
|
115
91
|
end
|
116
92
|
return nil unless recipients > 0
|
117
93
|
|
@@ -123,8 +99,7 @@ module Sisimai::Lhost
|
|
123
99
|
e['agent'] = self.smtpagent
|
124
100
|
end
|
125
101
|
|
126
|
-
|
127
|
-
return { 'ds' => dscontents, 'rfc822' => rfc822part }
|
102
|
+
return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
|
128
103
|
end
|
129
104
|
|
130
105
|
end
|
@@ -9,9 +9,9 @@ module Sisimai::Lhost
|
|
9
9
|
|
10
10
|
# https://aws.amazon.com/ses/
|
11
11
|
Indicators = Sisimai::Lhost.INDICATORS
|
12
|
+
ReBackbone = %r|^content-type:[ ]message/rfc822|.freeze
|
12
13
|
StartingOf = {
|
13
14
|
message: ['The following message to <', 'An error occurred while trying to deliver the mail'],
|
14
|
-
rfc822: ['content-type: message/rfc822'],
|
15
15
|
}.freeze
|
16
16
|
MessagesOf = { 'expired' => ['Delivery expired'] }.freeze
|
17
17
|
|
@@ -36,17 +36,31 @@ module Sisimai::Lhost
|
|
36
36
|
# part or nil if it failed to parse or
|
37
37
|
# the arguments are missing
|
38
38
|
def make(mhead, mbody)
|
39
|
+
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
40
|
+
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
41
|
+
|
39
42
|
if mbody.start_with?('{')
|
40
43
|
# The message body is JSON string
|
41
44
|
return nil unless mhead['x-amz-sns-message-id']
|
42
45
|
return nil if mhead['x-amz-sns-message-id'].empty?
|
43
46
|
|
44
|
-
|
47
|
+
# https://docs.aws.amazon.com/en_us/ses/latest/DeveloperGuide/notification-contents.html
|
48
|
+
bouncetype = {
|
49
|
+
'Permanent' => { 'General' => '', 'NoEmail' => '', 'Suppressed' => '' },
|
50
|
+
'Transient' => {
|
51
|
+
'General' => '',
|
52
|
+
'MailboxFull' => 'mailboxfull',
|
53
|
+
'MessageTooLarge' => 'mesgtoobig',
|
54
|
+
'ContentRejected' => '',
|
55
|
+
'AttachmentRejected' => '',
|
56
|
+
},
|
57
|
+
}.freeze
|
45
58
|
jsonstring = ''
|
46
59
|
sespayload = nil
|
47
60
|
foldedline = false
|
61
|
+
bodyslices = mbody.split("\n")
|
48
62
|
|
49
|
-
while e =
|
63
|
+
while e = bodyslices.shift do
|
50
64
|
# Find JSON string from the message body
|
51
65
|
next if e.empty?
|
52
66
|
break if e == '--'
|
@@ -88,10 +102,136 @@ module Sisimai::Lhost
|
|
88
102
|
return nil
|
89
103
|
end
|
90
104
|
|
91
|
-
|
105
|
+
rfc822head = {} # (Hash) Check flags for headers in RFC822 part
|
106
|
+
labeltable = {
|
107
|
+
'Bounce' => 'bouncedRecipients',
|
108
|
+
'Complaint' => 'complainedRecipients',
|
109
|
+
}
|
110
|
+
p = sespayload
|
111
|
+
v = nil
|
112
|
+
|
113
|
+
if %w[Bounce Complaint].index(p['notificationType'])
|
114
|
+
# { "notificationType":"Bounce", "bounce": { "bounceType":"Permanent",...
|
115
|
+
o = p[p['notificationType'].downcase].dup
|
116
|
+
r = o[labeltable[p['notificationType']]] || []
|
117
|
+
|
118
|
+
while e = r.shift do
|
119
|
+
# 'bouncedRecipients' => [ { 'emailAddress' => 'bounce@si...' }, ... ]
|
120
|
+
# 'complainedRecipients' => [ { 'emailAddress' => 'complaint@si...' }, ... ]
|
121
|
+
next unless Sisimai::RFC5322.is_emailaddress(e['emailAddress'])
|
122
|
+
|
123
|
+
v = dscontents[-1]
|
124
|
+
if v['recipient']
|
125
|
+
# There are multiple recipient addresses in the message body.
|
126
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
127
|
+
v = dscontents[-1]
|
128
|
+
end
|
129
|
+
recipients += 1
|
130
|
+
v['recipient'] = e['emailAddress']
|
131
|
+
|
132
|
+
if p['notificationType'] == 'Bounce'
|
133
|
+
# 'bouncedRecipients => [ {
|
134
|
+
# 'emailAddress' => 'bounce@simulator.amazonses.com',
|
135
|
+
# 'action' => 'failed',
|
136
|
+
# 'status' => '5.1.1',
|
137
|
+
# 'diagnosticCode' => 'smtp; 550 5.1.1 user unknown'
|
138
|
+
# }, ... ]
|
139
|
+
v['action'] = e['action']
|
140
|
+
v['status'] = e['status']
|
141
|
+
|
142
|
+
if cv = e['diagnosticCode'].match(/\A(.+?);[ ]*(.+)\z/)
|
143
|
+
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
144
|
+
v['spec'] = cv[1].upcase
|
145
|
+
v['diagnosis'] = cv[2]
|
146
|
+
else
|
147
|
+
v['diagnosis'] = e['diagnosticCode']
|
148
|
+
end
|
149
|
+
|
150
|
+
# 'reportingMTA' => 'dsn; a27-23.smtp-out.us-west-2.amazonses.com',
|
151
|
+
if cv = o['reportingMTA'].match(/\Adsn;[ ](.+)\z/) then v['lhost'] = cv[1] end
|
152
|
+
|
153
|
+
if bouncetype[o['bounceType']] &&
|
154
|
+
bouncetype[o['bounceType']][o['bounceSubType']]
|
155
|
+
# 'bounce' => {
|
156
|
+
# 'bounceType' => 'Permanent',
|
157
|
+
# 'bounceSubType' => 'General'
|
158
|
+
# },
|
159
|
+
v['reason'] = bouncetype[o['bounceType']][o['bounceSubType']]
|
160
|
+
end
|
161
|
+
else
|
162
|
+
# 'complainedRecipients' => [ {
|
163
|
+
# 'emailAddress' => 'complaint@simulator.amazonses.com' }, ... ],
|
164
|
+
v['reason'] = 'feedback'
|
165
|
+
v['feedbacktype'] = o['complaintFeedbackType'] || ''
|
166
|
+
end
|
167
|
+
|
168
|
+
v['date'] = o['timestamp'] || p['mail']['timestamp']
|
169
|
+
v['date'].sub!(/[.]\d+Z\z/, '')
|
170
|
+
end
|
171
|
+
elsif p['notificationType'] == 'Delivery'
|
172
|
+
# { "notificationType":"Delivery", "delivery": { ...
|
173
|
+
o = p['delivery'].dup
|
174
|
+
r = o['recipients'] || []
|
175
|
+
|
176
|
+
while e = r.shift do
|
177
|
+
# 'delivery' => {
|
178
|
+
# 'timestamp' => '2016-11-23T12:01:03.512Z',
|
179
|
+
# 'processingTimeMillis' => 3982,
|
180
|
+
# 'reportingMTA' => 'a27-29.smtp-out.us-west-2.amazonses.com',
|
181
|
+
# 'recipients' => [
|
182
|
+
# 'success@simulator.amazonses.com'
|
183
|
+
# ],
|
184
|
+
# 'smtpResponse' => '250 2.6.0 Message received'
|
185
|
+
# },
|
186
|
+
next unless Sisimai::RFC5322.is_emailaddress(e)
|
187
|
+
|
188
|
+
v = dscontents[-1]
|
189
|
+
if v['recipient']
|
190
|
+
# There are multiple recipient addresses in the message body.
|
191
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
192
|
+
v = dscontents[-1]
|
193
|
+
end
|
194
|
+
recipients += 1
|
195
|
+
v['recipient'] = e
|
196
|
+
v['lhost'] = o['reportingMTA'] || ''
|
197
|
+
v['diagnosis'] = o['smtpResponse'] || ''
|
198
|
+
v['status'] = Sisimai::SMTP::Status.find(v['diagnosis']) || ''
|
199
|
+
v['replycode'] = Sisimai::SMTP::Reply.find(v['diagnosis']) || ''
|
200
|
+
v['reason'] = 'delivered'
|
201
|
+
v['action'] = 'deliverable'
|
202
|
+
|
203
|
+
v['date'] = o['timestamp'] || p['mail']['timestamp']
|
204
|
+
v['date'].sub!(/[.]\d+Z\z/, '')
|
205
|
+
end
|
206
|
+
else
|
207
|
+
# The value of "notificationType" is not any of "Bounce", "Complaint",
|
208
|
+
# or "Delivery".
|
209
|
+
return nil
|
210
|
+
end
|
211
|
+
return nil unless recipients > 0
|
212
|
+
|
213
|
+
dscontents.each { |e| e['agent'] = self.smtpagent }
|
214
|
+
if p['mail']['headers']
|
215
|
+
# "headersTruncated":false,
|
216
|
+
# "headers":[ { ...
|
217
|
+
p['mail']['headers'].each do |e|
|
218
|
+
# 'headers' => [ { 'name' => 'From', 'value' => 'neko@nyaan.jp' }, ... ],
|
219
|
+
next unless %w[From To Subject Message-ID Date].index(e['name'])
|
220
|
+
rfc822head[e['name'].downcase] = e['value']
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
unless rfc822head['message-id']
|
225
|
+
# Try to get the value of "Message-Id".
|
226
|
+
if p['mail']['messageId']
|
227
|
+
# 'messageId' => '01010157e48f9b9b-891e9a0e-9c9d-4773-9bfe-608f2ef4756d-000000'
|
228
|
+
rfc822head['message-id'] = p['mail']['messageId']
|
229
|
+
end
|
230
|
+
end
|
231
|
+
return { 'ds' => dscontents, 'rfc822' => rfc822head }
|
232
|
+
|
92
233
|
else
|
93
234
|
# The message body is an email
|
94
|
-
|
95
235
|
# :from => %r/\AMAILER-DAEMON[@]email[-]bounces[.]amazonses[.]com\z/,
|
96
236
|
# :subject => %r/\ADelivery Status Notification [(]Failure[)]\z/,
|
97
237
|
return nil if mhead['x-mailer'].to_s.start_with?('Amazon WorkMail')
|
@@ -105,19 +245,16 @@ module Sisimai::Lhost
|
|
105
245
|
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
106
246
|
permessage = {} # (Hash) Store values of each Per-Message field
|
107
247
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
rfc822list = [] # (Array) Each line in message/rfc822 part string
|
112
|
-
blanklines = 0 # (Integer) The number of blank lines
|
248
|
+
emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
|
249
|
+
bodyslices = emailsteak[0].split("\n")
|
250
|
+
readslices = ['']
|
113
251
|
readcursor = 0 # (Integer) Points the current cursor position
|
114
|
-
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
115
252
|
v = nil
|
116
253
|
|
117
|
-
while e =
|
118
|
-
#
|
119
|
-
|
120
|
-
|
254
|
+
while e = bodyslices.shift do
|
255
|
+
# Read error messages and delivery status lines from the head of the email
|
256
|
+
# to the previous line of the beginning of the original message.
|
257
|
+
readslices << e # Save the current line for the next loop
|
121
258
|
|
122
259
|
if readcursor == 0
|
123
260
|
# Beginning of the bounce message or message/delivery-status part
|
@@ -126,69 +263,49 @@ module Sisimai::Lhost
|
|
126
263
|
next
|
127
264
|
end
|
128
265
|
end
|
266
|
+
next if (readcursor & Indicators[:deliverystatus]) == 0
|
267
|
+
next if e.empty?
|
129
268
|
|
130
|
-
if
|
131
|
-
#
|
132
|
-
|
133
|
-
|
134
|
-
next
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
if readcursor & Indicators[:'message-rfc822'] > 0
|
139
|
-
# message/rfc822 OR text/rfc822-headers part
|
140
|
-
if e.empty?
|
141
|
-
blanklines += 1
|
142
|
-
break if blanklines > 1
|
143
|
-
next
|
144
|
-
end
|
145
|
-
rfc822list << e
|
146
|
-
else
|
147
|
-
# message/delivery-status part
|
148
|
-
next if (readcursor & Indicators[:deliverystatus]) == 0
|
149
|
-
next if e.empty?
|
150
|
-
|
151
|
-
if f = Sisimai::RFC1894.match(e)
|
152
|
-
# "e" matched with any field defined in RFC3464
|
153
|
-
next unless o = Sisimai::RFC1894.field(e)
|
154
|
-
v = dscontents[-1]
|
269
|
+
if f = Sisimai::RFC1894.match(e)
|
270
|
+
# "e" matched with any field defined in RFC3464
|
271
|
+
next unless o = Sisimai::RFC1894.field(e)
|
272
|
+
v = dscontents[-1]
|
155
273
|
|
156
|
-
|
274
|
+
if o[-1] == 'addr'
|
275
|
+
# Final-Recipient: rfc822; kijitora@example.jp
|
276
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
277
|
+
if o[0] == 'final-recipient'
|
157
278
|
# Final-Recipient: rfc822; kijitora@example.jp
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
# There are multiple recipient addresses in the message body.
|
163
|
-
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
164
|
-
v = dscontents[-1]
|
165
|
-
end
|
166
|
-
v['recipient'] = o[2]
|
167
|
-
recipients += 1
|
168
|
-
else
|
169
|
-
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
170
|
-
v['alias'] = o[2]
|
279
|
+
if v['recipient']
|
280
|
+
# There are multiple recipient addresses in the message body.
|
281
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
282
|
+
v = dscontents[-1]
|
171
283
|
end
|
172
|
-
|
173
|
-
|
174
|
-
v['spec'] = o[1]
|
175
|
-
v['diagnosis'] = o[2]
|
284
|
+
v['recipient'] = o[2]
|
285
|
+
recipients += 1
|
176
286
|
else
|
177
|
-
#
|
178
|
-
|
179
|
-
v[fieldtable[o[0]]] = o[2]
|
180
|
-
|
181
|
-
next unless f == 1
|
182
|
-
permessage[fieldtable[o[0]]] = o[2]
|
287
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
288
|
+
v['alias'] = o[2]
|
183
289
|
end
|
290
|
+
elsif o[-1] == 'code'
|
291
|
+
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
292
|
+
v['spec'] = o[1]
|
293
|
+
v['diagnosis'] = o[2]
|
184
294
|
else
|
185
|
-
#
|
186
|
-
next unless
|
187
|
-
|
188
|
-
|
189
|
-
|
295
|
+
# Other DSN fields defined in RFC3464
|
296
|
+
next unless fieldtable[o[0]]
|
297
|
+
v[fieldtable[o[0]]] = o[2]
|
298
|
+
|
299
|
+
next unless f == 1
|
300
|
+
permessage[fieldtable[o[0]]] = o[2]
|
190
301
|
end
|
191
|
-
|
302
|
+
else
|
303
|
+
# Continued line of the value of Diagnostic-Code field
|
304
|
+
next unless readslices[-2].start_with?('Diagnostic-Code:')
|
305
|
+
next unless cv = e.match(/\A[ \t]+(.+)\z/)
|
306
|
+
v['diagnosis'] << ' ' << cv[1]
|
307
|
+
readslices[-1] = 'Diagnostic-Code: ' << e
|
308
|
+
end
|
192
309
|
end
|
193
310
|
|
194
311
|
if recipients == 0 && mbody =~ /notificationType/
|
@@ -228,175 +345,12 @@ module Sisimai::Lhost
|
|
228
345
|
e['reason'] = r
|
229
346
|
break
|
230
347
|
end
|
231
|
-
|
232
348
|
end
|
233
349
|
|
234
|
-
|
235
|
-
return { 'ds' => dscontents, 'rfc822' => rfc822part }
|
350
|
+
return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
|
236
351
|
end # END of a parser for email message
|
237
|
-
end # END of def make()
|
238
|
-
|
239
|
-
# @abstract Adapt Amazon SES bounce object for Sisimai::Message format
|
240
|
-
# @param [Hash] argvs bounce object(JSON) retrieved from Amazon SNS
|
241
|
-
# @return [Hash, Nil] Bounce data list and message/rfc822 part or
|
242
|
-
# nil if it failed to parse or the
|
243
|
-
# arguments are missing
|
244
|
-
# @since v4.20.0
|
245
|
-
# @until v4.25.5
|
246
|
-
def json(argvs)
|
247
|
-
return nil unless argvs.is_a? Hash
|
248
|
-
return nil if argvs.empty?
|
249
|
-
return nil unless argvs.key?('notificationType')
|
250
|
-
|
251
|
-
# https://docs.aws.amazon.com/en_us/ses/latest/DeveloperGuide/notification-contents.html
|
252
|
-
bouncetype = {
|
253
|
-
'Permanent' => {
|
254
|
-
'General' => '',
|
255
|
-
'NoEmail' => '',
|
256
|
-
'Suppressed' => '',
|
257
|
-
},
|
258
|
-
'Transient' => {
|
259
|
-
'General' => '',
|
260
|
-
'MailboxFull' => 'mailboxfull',
|
261
|
-
'MessageTooLarge' => 'mesgtoobig',
|
262
|
-
'ContentRejected' => '',
|
263
|
-
'AttachmentRejected' => '',
|
264
|
-
},
|
265
|
-
}.freeze
|
266
|
-
|
267
|
-
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
268
|
-
rfc822head = {} # (Hash) Check flags for headers in RFC822 part
|
269
|
-
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
270
|
-
labeltable = {
|
271
|
-
'Bounce' => 'bouncedRecipients',
|
272
|
-
'Complaint' => 'complainedRecipients',
|
273
|
-
}
|
274
|
-
v = nil
|
275
|
-
|
276
|
-
if %w[Bounce Complaint].index(argvs['notificationType'])
|
277
|
-
# { "notificationType":"Bounce", "bounce": { "bounceType":"Permanent",...
|
278
|
-
o = argvs[argvs['notificationType'].downcase].dup
|
279
|
-
r = o[labeltable[argvs['notificationType']]] || []
|
280
|
-
|
281
|
-
while e = r.shift do
|
282
|
-
# 'bouncedRecipients' => [ { 'emailAddress' => 'bounce@si...' }, ... ]
|
283
|
-
# 'complainedRecipients' => [ { 'emailAddress' => 'complaint@si...' }, ... ]
|
284
|
-
next unless Sisimai::RFC5322.is_emailaddress(e['emailAddress'])
|
285
|
-
|
286
|
-
v = dscontents[-1]
|
287
|
-
if v['recipient']
|
288
|
-
# There are multiple recipient addresses in the message body.
|
289
|
-
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
290
|
-
v = dscontents[-1]
|
291
|
-
end
|
292
|
-
recipients += 1
|
293
|
-
v['recipient'] = e['emailAddress']
|
294
|
-
|
295
|
-
if argvs['notificationType'] == 'Bounce'
|
296
|
-
# 'bouncedRecipients => [ {
|
297
|
-
# 'emailAddress' => 'bounce@simulator.amazonses.com',
|
298
|
-
# 'action' => 'failed',
|
299
|
-
# 'status' => '5.1.1',
|
300
|
-
# 'diagnosticCode' => 'smtp; 550 5.1.1 user unknown'
|
301
|
-
# }, ... ]
|
302
|
-
v['action'] = e['action']
|
303
|
-
v['status'] = e['status']
|
304
|
-
|
305
|
-
if cv = e['diagnosticCode'].match(/\A(.+?);[ ]*(.+)\z/)
|
306
|
-
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
307
|
-
v['spec'] = cv[1].upcase
|
308
|
-
v['diagnosis'] = cv[2]
|
309
|
-
else
|
310
|
-
v['diagnosis'] = e['diagnosticCode']
|
311
|
-
end
|
312
|
-
|
313
|
-
# 'reportingMTA' => 'dsn; a27-23.smtp-out.us-west-2.amazonses.com',
|
314
|
-
if cv = o['reportingMTA'].match(/\Adsn;[ ](.+)\z/) then v['lhost'] = cv[1] end
|
315
|
-
|
316
|
-
if bouncetype.key?(o['bounceType']) &&
|
317
|
-
bouncetype[o['bounceType']].key?(o['bounceSubType'])
|
318
|
-
# 'bounce' => {
|
319
|
-
# 'bounceType' => 'Permanent',
|
320
|
-
# 'bounceSubType' => 'General'
|
321
|
-
# },
|
322
|
-
v['reason'] = bouncetype[o['bounceType']][o['bounceSubType']]
|
323
|
-
end
|
324
|
-
else
|
325
|
-
# 'complainedRecipients' => [ {
|
326
|
-
# 'emailAddress' => 'complaint@simulator.amazonses.com' }, ... ],
|
327
|
-
v['reason'] = 'feedback'
|
328
|
-
v['feedbacktype'] = o['complaintFeedbackType'] || ''
|
329
|
-
end
|
330
|
-
|
331
|
-
v['date'] = o['timestamp'] || argvs['mail']['timestamp']
|
332
|
-
v['date'].sub!(/[.]\d+Z\z/, '')
|
333
|
-
end
|
334
|
-
elsif argvs['notificationType'] == 'Delivery'
|
335
|
-
# { "notificationType":"Delivery", "delivery": { ...
|
336
|
-
o = argvs['delivery'].dup
|
337
|
-
r = o['recipients'] || []
|
338
|
-
|
339
|
-
while e = r.shift do
|
340
|
-
# 'delivery' => {
|
341
|
-
# 'timestamp' => '2016-11-23T12:01:03.512Z',
|
342
|
-
# 'processingTimeMillis' => 3982,
|
343
|
-
# 'reportingMTA' => 'a27-29.smtp-out.us-west-2.amazonses.com',
|
344
|
-
# 'recipients' => [
|
345
|
-
# 'success@simulator.amazonses.com'
|
346
|
-
# ],
|
347
|
-
# 'smtpResponse' => '250 2.6.0 Message received'
|
348
|
-
# },
|
349
|
-
next unless Sisimai::RFC5322.is_emailaddress(e)
|
350
|
-
|
351
|
-
v = dscontents[-1]
|
352
|
-
if v['recipient']
|
353
|
-
# There are multiple recipient addresses in the message body.
|
354
|
-
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
355
|
-
v = dscontents[-1]
|
356
|
-
end
|
357
|
-
recipients += 1
|
358
|
-
v['recipient'] = e
|
359
|
-
v['lhost'] = o['reportingMTA'] || ''
|
360
|
-
v['diagnosis'] = o['smtpResponse'] || ''
|
361
|
-
v['status'] = Sisimai::SMTP::Status.find(v['diagnosis']) || ''
|
362
|
-
v['replycode'] = Sisimai::SMTP::Reply.find(v['diagnosis']) || ''
|
363
|
-
v['reason'] = 'delivered'
|
364
|
-
v['action'] = 'deliverable'
|
365
|
-
|
366
|
-
v['date'] = o['timestamp'] || argvs['mail']['timestamp']
|
367
|
-
v['date'].sub!(/[.]\d+Z\z/, '')
|
368
|
-
end
|
369
|
-
else
|
370
|
-
# The value of "notificationType" is not any of "Bounce", "Complaint",
|
371
|
-
# or "Delivery".
|
372
|
-
return nil
|
373
|
-
end
|
374
|
-
return nil unless recipients > 0
|
375
|
-
|
376
|
-
dscontents.each do |e|
|
377
|
-
e['agent'] = self.smtpagent
|
378
|
-
end
|
379
|
-
|
380
|
-
if argvs['mail']['headers']
|
381
|
-
# "headersTruncated":false,
|
382
|
-
# "headers":[ { ...
|
383
|
-
argvs['mail']['headers'].each do |e|
|
384
|
-
# 'headers' => [ { 'name' => 'From', 'value' => 'neko@nyaan.jp' }, ... ],
|
385
|
-
next unless %w[From To Subject Message-ID Date].index(e['name'])
|
386
|
-
rfc822head[e['name'].downcase] = e['value']
|
387
|
-
end
|
388
|
-
end
|
389
|
-
|
390
|
-
unless rfc822head['message-id']
|
391
|
-
# Try to get the value of "Message-Id".
|
392
|
-
if argvs['mail']['messageId']
|
393
|
-
# 'messageId' => '01010157e48f9b9b-891e9a0e-9c9d-4773-9bfe-608f2ef4756d-000000'
|
394
|
-
rfc822head['message-id'] = argvs['mail']['messageId']
|
395
|
-
end
|
396
|
-
end
|
397
|
-
return { 'ds' => dscontents, 'rfc822' => rfc822head }
|
398
|
-
end
|
399
352
|
|
353
|
+
end # END of def make()
|
400
354
|
end
|
401
355
|
end
|
402
356
|
end
|