sisimai 4.25.3 → 4.25.4
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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +1 -0
- data/ANALYTICAL-PRECISION +2 -4
- data/CONTRIBUTING +4 -0
- data/ChangeLog.md +39 -0
- data/Developers.mk +1 -72
- data/Makefile +4 -0
- data/README-JA.md +3 -0
- data/README.md +5 -2
- data/lib/sisimai.rb +4 -3
- data/lib/sisimai/arf.rb +6 -6
- data/lib/sisimai/bite.rb +8 -3
- data/lib/sisimai/bite/email.rb +7 -50
- data/lib/sisimai/bite/json.rb +5 -25
- data/lib/sisimai/data.rb +1 -1
- data/lib/sisimai/lhost.rb +101 -0
- data/lib/sisimai/{bite/email → lhost}/activehunter.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/amavis.rb +9 -9
- data/lib/sisimai/lhost/amazonses.rb +403 -0
- data/lib/sisimai/{bite/email → lhost}/amazonworkmail.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/aol.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/apachejames.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/bigfoot.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/biglobe.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/courier.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/domino.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/einsundeins.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/exchange2003.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/exchange2007.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/exim.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/ezweb.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/facebook.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/fml.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/gmx.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/google.rb +14 -14
- data/lib/sisimai/{bite/email → lhost}/gsuite.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/imailserver.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/interscanmss.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/kddi.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/mailfoundry.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/mailmarshalsmtp.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/mailru.rb +11 -11
- data/lib/sisimai/{bite/email → lhost}/mcafee.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/messagelabs.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/messagingserver.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/mfilter.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/mxlogic.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/notes.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/office365.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/opensmtpd.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/outlook.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/postfix.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/qmail.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/receivingses.rb +11 -11
- data/lib/sisimai/{bite/email → lhost}/sendgrid.rb +120 -11
- data/lib/sisimai/{bite/email → lhost}/sendmail.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/surfcontrol.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/userdefined.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/v5sendmail.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/verizon.rb +11 -11
- data/lib/sisimai/{bite/email → lhost}/x1.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/x2.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/x3.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/x4.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/x5.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/yahoo.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/yandex.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/zoho.rb +10 -10
- data/lib/sisimai/mda.rb +1 -1
- data/lib/sisimai/message.rb +535 -28
- data/lib/sisimai/message/email.rb +3 -454
- data/lib/sisimai/message/json.rb +3 -177
- data/lib/sisimai/order.rb +238 -4
- data/lib/sisimai/order/email.rb +5 -190
- data/lib/sisimai/order/json.rb +3 -39
- data/lib/sisimai/reason.rb +1 -1
- data/lib/sisimai/reason/norelaying.rb +1 -1
- data/lib/sisimai/reason/userunknown.rb +1 -1
- data/lib/sisimai/reason/virusdetected.rb +1 -0
- data/lib/sisimai/rfc1894.rb +2 -2
- data/lib/sisimai/rfc3464.rb +13 -13
- data/lib/sisimai/rfc3834.rb +3 -3
- data/lib/sisimai/rfc5322.rb +1 -1
- data/lib/sisimai/rhost.rb +5 -4
- data/lib/sisimai/rhost/exchangeonline.rb +1 -1
- data/lib/sisimai/rhost/franceptt.rb +1 -1
- data/lib/sisimai/rhost/godaddy.rb +1 -1
- data/lib/sisimai/rhost/iua.rb +39 -0
- data/lib/sisimai/version.rb +1 -1
- data/set-of-emails/README.md +0 -1
- data/set-of-emails/maildir/bsd/email-gsuite-13.eml +261 -0
- data/set-of-emails/maildir/bsd/email-postfix-62.eml +105 -0
- data/set-of-emails/maildir/bsd/email-postfix-63.eml +85 -0
- data/set-of-emails/maildir/bsd/rhost-iua-01.eml +70 -0
- metadata +62 -82
- data/fig/sisimai-architecture.png +0 -0
- data/lib/sisimai/bite/email/amazonses.rb +0 -186
- data/lib/sisimai/bite/json/amazonses.rb +0 -242
- data/lib/sisimai/bite/json/sendgrid.rb +0 -123
- data/set-of-emails/jsonobj/json-amazonses-01.json +0 -1
- data/set-of-emails/jsonobj/json-amazonses-02.json +0 -11
- data/set-of-emails/jsonobj/json-amazonses-03.json +0 -1
- data/set-of-emails/jsonobj/json-amazonses-04.json +0 -1
- data/set-of-emails/jsonobj/json-amazonses-05.json +0 -1
- data/set-of-emails/jsonobj/json-amazonses-06.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-01.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-02.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-03.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-04.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-05.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-06.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-07.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-08.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-09.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-10.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-11.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-12.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-13.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-14.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-15.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-16.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-17.json +0 -1
data/lib/sisimai/data.rb
CHANGED
@@ -236,7 +236,7 @@ module Sisimai
|
|
236
236
|
# Subject: header of the original message
|
237
237
|
p['subject'] = rfc822data['subject'] || ''
|
238
238
|
p['subject'].scrub!('?')
|
239
|
-
p['subject'].chomp!("\r") if p['subject'].end_with?("\r")
|
239
|
+
p['subject'].chomp!("\r") if p['subject'].end_with?("\r")
|
240
240
|
|
241
241
|
# The value of "List-Id" header
|
242
242
|
p['listid'] = rfc822data['list-id'] || ''
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Sisimai
|
2
|
+
# Sisimai::Lhost - Base class for Sisimai::Lhost::*
|
3
|
+
module Lhost
|
4
|
+
class << self
|
5
|
+
# Imported from p5-Sisimail/lib/Sisimai/Lhost.pm
|
6
|
+
require 'sisimai/rfc5322'
|
7
|
+
|
8
|
+
# Data structure for parsed bounce messages
|
9
|
+
# @return [Hash] Data structure for delivery status
|
10
|
+
# @private
|
11
|
+
def DELIVERYSTATUS
|
12
|
+
return {
|
13
|
+
'spec' => nil, # Protocl specification
|
14
|
+
'date' => nil, # The value of Last-Attempt-Date header
|
15
|
+
'rhost' => nil, # The value of Remote-MTA header
|
16
|
+
'lhost' => nil, # The value of Received-From-MTA header
|
17
|
+
'alias' => nil, # The value of alias entry(RHS)
|
18
|
+
'agent' => nil, # MTA module name
|
19
|
+
'action' => nil, # The value of Action header
|
20
|
+
'status' => nil, # The value of Status header
|
21
|
+
'reason' => nil, # Temporary reason of bounce
|
22
|
+
'command' => nil, # SMTP command in the message body
|
23
|
+
'replycode' => nil, # SMTP Reply code
|
24
|
+
'diagnosis' => nil, # The value of Diagnostic-Code header
|
25
|
+
'recipient' => nil, # The value of Final-Recipient header
|
26
|
+
'softbounce' => nil, # Soft bounce or not
|
27
|
+
'feedbacktype' => nil, # Feedback Type
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
# @abstract Flags for position variable
|
32
|
+
# @return [Hash] Position flag data
|
33
|
+
# @private
|
34
|
+
def INDICATORS
|
35
|
+
return {
|
36
|
+
:'deliverystatus' => (1 << 1),
|
37
|
+
:'message-rfc822' => (1 << 2),
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def smtpagent(v = '')
|
42
|
+
return v.to_s.sub(/\ASisimai::Lhost::/, 'Email::')
|
43
|
+
end
|
44
|
+
def description; return ''; end
|
45
|
+
def headerlist; return []; end
|
46
|
+
def removedat; return 'v4.25.5'; end # This method will be removed at the future release of Sisimai
|
47
|
+
|
48
|
+
# @abstract MTA list
|
49
|
+
# @return [Array] MTA list with order
|
50
|
+
def index
|
51
|
+
return %w[
|
52
|
+
Sendmail Postfix Qmail Exim Courier OpenSMTPD Office365 Outlook
|
53
|
+
Exchange2007 Exchange2003 Yahoo GSuite Aol SendGrid AmazonSES MailRu
|
54
|
+
Yandex MessagingServer Domino Notes ReceivingSES AmazonWorkMail Verizon
|
55
|
+
GMX Bigfoot Facebook Zoho EinsUndEins MessageLabs EZweb KDDI Biglobe
|
56
|
+
Amavis ApacheJames McAfee MXLogic MailFoundry IMailServer
|
57
|
+
MFILTER Activehunter InterScanMSS SurfControl MailMarshalSMTP
|
58
|
+
X1 X2 X3 X4 X5 V5sendmail FML Google]
|
59
|
+
end
|
60
|
+
|
61
|
+
# @abstract MTA list which have one or more extra headers
|
62
|
+
# @return [Array] MTA list (have extra headers)
|
63
|
+
def heads
|
64
|
+
return %w[
|
65
|
+
Exim Office365 Outlook Exchange2007 Exchange2003 GSuite SendGrid
|
66
|
+
AmazonSES ReceivingSES AmazonWorkMail Aol GMX MailRu MessageLabs Yahoo
|
67
|
+
Yandex Zoho EinsUndEins MXLogic McAfee MFILTER EZweb Activehunter IMailServer
|
68
|
+
SurfControl FML Google
|
69
|
+
]
|
70
|
+
end
|
71
|
+
|
72
|
+
# @abstract Parse bounce messages
|
73
|
+
# @param [Hash] mhead Message header of a bounce email
|
74
|
+
# @options mhead [String] from From header
|
75
|
+
# @options mhead [String] date Date header
|
76
|
+
# @options mhead [String] subject Subject header
|
77
|
+
# @options mhead [Array] received Received headers
|
78
|
+
# @options mhead [String] others Other required headers
|
79
|
+
# @param [String] mbody Message body of a bounce email
|
80
|
+
# @return [Hash, Nil] Bounce data list and message/rfc822
|
81
|
+
# part or nil if it failed to parse or
|
82
|
+
# the arguments are missing
|
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
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
@@ -1,20 +1,20 @@
|
|
1
|
-
module Sisimai::
|
2
|
-
# Sisimai::
|
3
|
-
#
|
1
|
+
module Sisimai::Lhost
|
2
|
+
# Sisimai::Lhost::Activehunter parses a bounce email which created by
|
3
|
+
# TransWARE Active!hunter.
|
4
4
|
# Methods in the module are called from only Sisimai::Message.
|
5
5
|
module Activehunter
|
6
6
|
class << self
|
7
|
-
# Imported from p5-Sisimail/lib/Sisimai/
|
8
|
-
require 'sisimai/
|
7
|
+
# Imported from p5-Sisimail/lib/Sisimai/Lhost/Activehunter.pm
|
8
|
+
require 'sisimai/lhost'
|
9
9
|
|
10
|
-
Indicators = Sisimai::
|
10
|
+
Indicators = Sisimai::Lhost.INDICATORS
|
11
11
|
StartingOf = {
|
12
12
|
message: [' ----- The following addresses had permanent fatal errors -----'],
|
13
13
|
rfc822: ['Content-type: message/rfc822'],
|
14
14
|
}.freeze
|
15
15
|
|
16
16
|
def description; return 'TransWARE Active!hunter'; end
|
17
|
-
def smtpagent; return Sisimai::
|
17
|
+
def smtpagent; return Sisimai::Lhost.smtpagent(self); end
|
18
18
|
def headerlist; return %w[x-ahmailid]; end
|
19
19
|
|
20
20
|
# Parse bounce messages from TransWARE Active!hunter
|
@@ -28,12 +28,12 @@ module Sisimai::Bite::Email
|
|
28
28
|
# @return [Hash, Nil] Bounce data list and message/rfc822
|
29
29
|
# part or nil if it failed to parse or
|
30
30
|
# the arguments are missing
|
31
|
-
def
|
31
|
+
def make(mhead, mbody)
|
32
32
|
# :from => %r/\A"MAILER-DAEMON"/,
|
33
33
|
# :subject => %r/FAILURE NOTICE :/,
|
34
34
|
return nil unless mhead['x-ahmailid']
|
35
35
|
|
36
|
-
dscontents = [Sisimai::
|
36
|
+
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
37
37
|
hasdivided = mbody.split("\n")
|
38
38
|
rfc822list = [] # (Array) Each line in message/rfc822 part string
|
39
39
|
blanklines = 0 # (Integer) The number of blank lines
|
@@ -83,7 +83,7 @@ module Sisimai::Bite::Email
|
|
83
83
|
# >>> kijitora@example.org <kijitora@example.org>
|
84
84
|
if v['recipient']
|
85
85
|
# There are multiple recipient addresses in the message body.
|
86
|
-
dscontents << Sisimai::
|
86
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
87
87
|
v = dscontents[-1]
|
88
88
|
end
|
89
89
|
v['recipient'] = cv[1]
|
@@ -1,19 +1,19 @@
|
|
1
|
-
module Sisimai::
|
2
|
-
# Sisimai::
|
1
|
+
module Sisimai::Lhost
|
2
|
+
# Sisimai::Lhost::Amavis parses a bounce email which created by
|
3
3
|
# amavsid-new. Methods in the module are called from only Sisimai::Message.
|
4
4
|
module Amavis
|
5
5
|
class << self
|
6
|
-
# Imported from p5-Sisimail/lib/Sisimai/
|
7
|
-
require 'sisimai/
|
6
|
+
# Imported from p5-Sisimail/lib/Sisimai/Lhost/Amavis.pm
|
7
|
+
require 'sisimai/lhost'
|
8
8
|
|
9
|
-
Indicators = Sisimai::
|
9
|
+
Indicators = Sisimai::Lhost.INDICATORS
|
10
10
|
StartingOf = {
|
11
11
|
message: ['The message '],
|
12
12
|
rfc822: ['Content-Type: text/rfc822-headers'],
|
13
13
|
}.freeze
|
14
14
|
|
15
15
|
def description; return 'amavisd-new: https://www.amavis.org/'; end
|
16
|
-
def smtpagent; return Sisimai::
|
16
|
+
def smtpagent; return Sisimai::Lhost.smtpagent(self); end
|
17
17
|
|
18
18
|
# Parse bounce messages from amavisd-new
|
19
19
|
# @param [Hash] mhead Message headers of a bounce email
|
@@ -26,7 +26,7 @@ module Sisimai::Bite::Email
|
|
26
26
|
# @return [Hash, Nil] Bounce data list and message/rfc822
|
27
27
|
# part or nil if it failed to parse or
|
28
28
|
# the arguments are missing
|
29
|
-
def
|
29
|
+
def make(mhead, mbody)
|
30
30
|
# From: "Content-filter at neko1.example.jp" <postmaster@neko1.example.jp>
|
31
31
|
# Subject: Undeliverable mail, MTA-BLOCKED
|
32
32
|
return nil unless mhead['from'].to_s.start_with?('"Content-filter at ')
|
@@ -35,7 +35,7 @@ module Sisimai::Bite::Email
|
|
35
35
|
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
36
36
|
permessage = {} # (Hash) Store values of each Per-Message field
|
37
37
|
|
38
|
-
dscontents = [Sisimai::
|
38
|
+
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
39
39
|
hasdivided = mbody.split("\n")
|
40
40
|
havepassed = ['']
|
41
41
|
rfc822list = [] # (Array) Each line in message/rfc822 part string
|
@@ -90,7 +90,7 @@ module Sisimai::Bite::Email
|
|
90
90
|
# Final-Recipient: rfc822; kijitora@example.jp
|
91
91
|
if v['recipient']
|
92
92
|
# There are multiple recipient addresses in the message body.
|
93
|
-
dscontents << Sisimai::
|
93
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
94
94
|
v = dscontents[-1]
|
95
95
|
end
|
96
96
|
v['recipient'] = o[2]
|
@@ -0,0 +1,403 @@
|
|
1
|
+
module Sisimai::Lhost
|
2
|
+
# Sisimai::Lhost::AmazonSES parses a bounce email which created by
|
3
|
+
# Amazon Simple Email Service.
|
4
|
+
# Methods in the module are called from only Sisimai::Message.
|
5
|
+
module AmazonSES
|
6
|
+
class << self
|
7
|
+
# Imported from p5-Sisimail/lib/Sisimai/Lhost/AmazonSES.pm
|
8
|
+
require 'sisimai/lhost'
|
9
|
+
|
10
|
+
# https://aws.amazon.com/ses/
|
11
|
+
Indicators = Sisimai::Lhost.INDICATORS
|
12
|
+
StartingOf = {
|
13
|
+
message: ['The following message to <', 'An error occurred while trying to deliver the mail'],
|
14
|
+
rfc822: ['content-type: message/rfc822'],
|
15
|
+
}.freeze
|
16
|
+
MessagesOf = { 'expired' => ['Delivery expired'] }.freeze
|
17
|
+
|
18
|
+
def description; return 'Amazon SES(Sending): https://aws.amazon.com/ses/'; end
|
19
|
+
def smtpagent; return Sisimai::Lhost.smtpagent(self); end
|
20
|
+
|
21
|
+
# X-SenderID: Sendmail Sender-ID Filter v1.0.0 nijo.example.jp p7V3i843003008
|
22
|
+
# X-Original-To: 000001321defbd2a-788e31c8-2be1-422f-a8d4-cf7765cc9ed7-000000@email-bounces.amazonses.com
|
23
|
+
# X-AWS-Outgoing: 199.255.192.156
|
24
|
+
# X-SES-Outgoing: 2016.10.12-54.240.27.6
|
25
|
+
def headerlist; return %w[x-aws-outgoing x-ses-outgoing x-amz-sns-message-id]; end
|
26
|
+
|
27
|
+
# Parse bounce messages from Amazon SES
|
28
|
+
# @param [Hash] mhead Message headers of a bounce email
|
29
|
+
# @options mhead [String] from From header
|
30
|
+
# @options mhead [String] date Date header
|
31
|
+
# @options mhead [String] subject Subject header
|
32
|
+
# @options mhead [Array] received Received headers
|
33
|
+
# @options mhead [String] others Other required headers
|
34
|
+
# @param [String] mbody Message body of a bounce email
|
35
|
+
# @return [Hash, Nil] Bounce data list and message/rfc822
|
36
|
+
# part or nil if it failed to parse or
|
37
|
+
# the arguments are missing
|
38
|
+
def make(mhead, mbody)
|
39
|
+
if mbody.start_with?('{')
|
40
|
+
# The message body is JSON string
|
41
|
+
return nil unless mhead['x-amz-sns-message-id']
|
42
|
+
return nil if mhead['x-amz-sns-message-id'].empty?
|
43
|
+
|
44
|
+
hasdivided = mbody.split("\n")
|
45
|
+
jsonstring = ''
|
46
|
+
sespayload = nil
|
47
|
+
foldedline = false
|
48
|
+
|
49
|
+
while e = hasdivided.shift do
|
50
|
+
# Find JSON string from the message body
|
51
|
+
next if e.empty?
|
52
|
+
break if e == '--'
|
53
|
+
break if e == '__END_OF_EMAIL_MESSAGE__'
|
54
|
+
|
55
|
+
# The line starts with " ", continued from !\n.
|
56
|
+
e.lstrip! if foldedline
|
57
|
+
foldedline = false
|
58
|
+
|
59
|
+
if e.end_with?('!')
|
60
|
+
# ... long long line ...![\n]
|
61
|
+
e.chomp!('!')
|
62
|
+
foldedline = true
|
63
|
+
end
|
64
|
+
jsonstring << e
|
65
|
+
end
|
66
|
+
|
67
|
+
begin
|
68
|
+
if RUBY_PLATFORM.start_with?('java')
|
69
|
+
# java-based ruby environment like JRuby.
|
70
|
+
require 'jrjackson'
|
71
|
+
jsonobject = JrJackson::Json.load(jsonstring)
|
72
|
+
|
73
|
+
# 'Message' => '{"notificationType":"Bounce",...
|
74
|
+
sespayload = JrJackson::Json.load(jsonobject['Message']) if jsonobject['Message']
|
75
|
+
else
|
76
|
+
# Matz' Ruby Implementation
|
77
|
+
require 'oj'
|
78
|
+
jsonobject = Oj.load(jsonstring)
|
79
|
+
|
80
|
+
# 'Message' => '{"notificationType":"Bounce",...
|
81
|
+
sespayload = Oj.load(jsonobject['Message']) if jsonobject['Message']
|
82
|
+
end
|
83
|
+
sespayload ||= jsonobject
|
84
|
+
|
85
|
+
rescue StandardError => ce
|
86
|
+
# Something wrong in decoding JSON
|
87
|
+
warn ' ***warning: Failed to decode JSON: ' << ce.to_s
|
88
|
+
return nil
|
89
|
+
end
|
90
|
+
|
91
|
+
return json(sespayload)
|
92
|
+
else
|
93
|
+
# The message body is an email
|
94
|
+
|
95
|
+
# :from => %r/\AMAILER-DAEMON[@]email[-]bounces[.]amazonses[.]com\z/,
|
96
|
+
# :subject => %r/\ADelivery Status Notification [(]Failure[)]\z/,
|
97
|
+
return nil if mhead['x-mailer'].to_s.start_with?('Amazon WorkMail')
|
98
|
+
|
99
|
+
match = 0
|
100
|
+
match += 1 if mhead['x-aws-outgoing']
|
101
|
+
match += 1 if mhead['x-ses-outgoing']
|
102
|
+
return nil unless match > 0
|
103
|
+
|
104
|
+
require 'sisimai/rfc1894'
|
105
|
+
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
106
|
+
permessage = {} # (Hash) Store values of each Per-Message field
|
107
|
+
|
108
|
+
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
109
|
+
hasdivided = mbody.split("\n")
|
110
|
+
havepassed = ['']
|
111
|
+
rfc822list = [] # (Array) Each line in message/rfc822 part string
|
112
|
+
blanklines = 0 # (Integer) The number of blank lines
|
113
|
+
readcursor = 0 # (Integer) Points the current cursor position
|
114
|
+
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
115
|
+
v = nil
|
116
|
+
|
117
|
+
while e = hasdivided.shift do
|
118
|
+
# Save the current line for the next loop
|
119
|
+
havepassed << e
|
120
|
+
p = havepassed[-2]
|
121
|
+
|
122
|
+
if readcursor == 0
|
123
|
+
# Beginning of the bounce message or message/delivery-status part
|
124
|
+
if e.start_with?(StartingOf[:message][0], StartingOf[:message][1])
|
125
|
+
readcursor |= Indicators[:deliverystatus]
|
126
|
+
next
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
if (readcursor & Indicators[:'message-rfc822']) == 0
|
131
|
+
# Beginning of the original message part(message/rfc822)
|
132
|
+
if e == StartingOf[:rfc822][0]
|
133
|
+
readcursor |= Indicators[:'message-rfc822']
|
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]
|
155
|
+
|
156
|
+
if o[-1] == 'addr'
|
157
|
+
# Final-Recipient: rfc822; kijitora@example.jp
|
158
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
159
|
+
if o[0] == 'final-recipient'
|
160
|
+
# Final-Recipient: rfc822; kijitora@example.jp
|
161
|
+
if v['recipient']
|
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]
|
171
|
+
end
|
172
|
+
elsif o[-1] == 'code'
|
173
|
+
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
174
|
+
v['spec'] = o[1]
|
175
|
+
v['diagnosis'] = o[2]
|
176
|
+
else
|
177
|
+
# Other DSN fields defined in RFC3464
|
178
|
+
next unless fieldtable.key?(o[0])
|
179
|
+
v[fieldtable[o[0]]] = o[2]
|
180
|
+
|
181
|
+
next unless f == 1
|
182
|
+
permessage[fieldtable[o[0]]] = o[2]
|
183
|
+
end
|
184
|
+
else
|
185
|
+
# Continued line of the value of Diagnostic-Code field
|
186
|
+
next unless p.start_with?('Diagnostic-Code:')
|
187
|
+
next unless cv = e.match(/\A[ \t]+(.+)\z/)
|
188
|
+
v['diagnosis'] << ' ' << cv[1]
|
189
|
+
havepassed[-1] = 'Diagnostic-Code: ' << e
|
190
|
+
end
|
191
|
+
end # End of message/delivery-status
|
192
|
+
end
|
193
|
+
|
194
|
+
if recipients == 0 && mbody =~ /notificationType/
|
195
|
+
# Try to parse with Sisimai::Lhost::AmazonSES module
|
196
|
+
j = Sisimai::Lhost::AmazonSES.json(mhead, mbody)
|
197
|
+
|
198
|
+
if j['ds'].is_a? Array
|
199
|
+
# Update dscontents
|
200
|
+
dscontents = j['ds']
|
201
|
+
recipients = j['ds'].size
|
202
|
+
end
|
203
|
+
end
|
204
|
+
return nil unless recipients > 0
|
205
|
+
|
206
|
+
dscontents.each do |e|
|
207
|
+
# Set default values if each value is empty.
|
208
|
+
e['lhost'] ||= permessage['rhost']
|
209
|
+
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
210
|
+
|
211
|
+
e['agent'] = self.smtpagent
|
212
|
+
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].to_s.tr("\n", ' '))
|
213
|
+
|
214
|
+
if e['status'].to_s.start_with?('5.0.0', '5.1.0', '4.0.0', '4.1.0')
|
215
|
+
# Get other D.S.N. value from the error message
|
216
|
+
errormessage = e['diagnosis']
|
217
|
+
|
218
|
+
if cv = e['diagnosis'].match(/["'](\d[.]\d[.]\d.+)['"]/)
|
219
|
+
# 5.1.0 - Unknown address error 550-'5.7.1 ...
|
220
|
+
errormessage = cv[1]
|
221
|
+
end
|
222
|
+
e['status'] = Sisimai::SMTP::Status.find(errormessage) || e['status']
|
223
|
+
end
|
224
|
+
|
225
|
+
MessagesOf.each_key do |r|
|
226
|
+
# Verify each regular expression of session errors
|
227
|
+
next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
|
228
|
+
e['reason'] = r
|
229
|
+
break
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
rfc822part = Sisimai::RFC5322.weedout(rfc822list)
|
235
|
+
return { 'ds' => dscontents, 'rfc822' => rfc822part }
|
236
|
+
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
|
+
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|