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
Binary file
|
@@ -1,186 +0,0 @@
|
|
1
|
-
module Sisimai::Bite::Email
|
2
|
-
# Sisimai::Bite::Email::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/Bite/Email/AmazonSES.pm
|
8
|
-
require 'sisimai/bite/email'
|
9
|
-
|
10
|
-
# http://aws.amazon.com/ses/
|
11
|
-
Indicators = Sisimai::Bite::Email.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): http://aws.amazon.com/ses/'; end
|
19
|
-
def smtpagent; return Sisimai::Bite.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 scan(mhead, mbody)
|
39
|
-
# :from => %r/\AMAILER-DAEMON[@]email[-]bounces[.]amazonses[.]com\z/,
|
40
|
-
# :subject => %r/\ADelivery Status Notification [(]Failure[)]\z/,
|
41
|
-
return nil if mhead['x-mailer'].to_s.start_with?('Amazon WorkMail')
|
42
|
-
|
43
|
-
match = 0
|
44
|
-
match += 1 if mhead['x-aws-outgoing']
|
45
|
-
match += 1 if mhead['x-ses-outgoing']
|
46
|
-
return nil unless match > 0
|
47
|
-
|
48
|
-
require 'sisimai/rfc1894'
|
49
|
-
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
50
|
-
permessage = {} # (Hash) Store values of each Per-Message field
|
51
|
-
|
52
|
-
dscontents = [Sisimai::Bite.DELIVERYSTATUS]
|
53
|
-
hasdivided = mbody.split("\n")
|
54
|
-
havepassed = ['']
|
55
|
-
rfc822list = [] # (Array) Each line in message/rfc822 part string
|
56
|
-
blanklines = 0 # (Integer) The number of blank lines
|
57
|
-
readcursor = 0 # (Integer) Points the current cursor position
|
58
|
-
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
59
|
-
v = nil
|
60
|
-
|
61
|
-
while e = hasdivided.shift do
|
62
|
-
# Save the current line for the next loop
|
63
|
-
havepassed << e
|
64
|
-
p = havepassed[-2]
|
65
|
-
|
66
|
-
if readcursor == 0
|
67
|
-
# Beginning of the bounce message or message/delivery-status part
|
68
|
-
if e.start_with?(StartingOf[:message][0], StartingOf[:message][1])
|
69
|
-
readcursor |= Indicators[:deliverystatus]
|
70
|
-
next
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
if (readcursor & Indicators[:'message-rfc822']) == 0
|
75
|
-
# Beginning of the original message part(message/rfc822)
|
76
|
-
if e == StartingOf[:rfc822][0]
|
77
|
-
readcursor |= Indicators[:'message-rfc822']
|
78
|
-
next
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
if readcursor & Indicators[:'message-rfc822'] > 0
|
83
|
-
# message/rfc822 OR text/rfc822-headers part
|
84
|
-
if e.empty?
|
85
|
-
blanklines += 1
|
86
|
-
break if blanklines > 1
|
87
|
-
next
|
88
|
-
end
|
89
|
-
rfc822list << e
|
90
|
-
else
|
91
|
-
# message/delivery-status part
|
92
|
-
next if (readcursor & Indicators[:deliverystatus]) == 0
|
93
|
-
next if e.empty?
|
94
|
-
|
95
|
-
if f = Sisimai::RFC1894.match(e)
|
96
|
-
# "e" matched with any field defined in RFC3464
|
97
|
-
next unless o = Sisimai::RFC1894.field(e)
|
98
|
-
v = dscontents[-1]
|
99
|
-
|
100
|
-
if o[-1] == 'addr'
|
101
|
-
# Final-Recipient: rfc822; kijitora@example.jp
|
102
|
-
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
103
|
-
if o[0] == 'final-recipient'
|
104
|
-
# Final-Recipient: rfc822; kijitora@example.jp
|
105
|
-
if v['recipient']
|
106
|
-
# There are multiple recipient addresses in the message body.
|
107
|
-
dscontents << Sisimai::Bite.DELIVERYSTATUS
|
108
|
-
v = dscontents[-1]
|
109
|
-
end
|
110
|
-
v['recipient'] = o[2]
|
111
|
-
recipients += 1
|
112
|
-
else
|
113
|
-
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
114
|
-
v['alias'] = o[2]
|
115
|
-
end
|
116
|
-
elsif o[-1] == 'code'
|
117
|
-
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
118
|
-
v['spec'] = o[1]
|
119
|
-
v['diagnosis'] = o[2]
|
120
|
-
else
|
121
|
-
# Other DSN fields defined in RFC3464
|
122
|
-
next unless fieldtable.key?(o[0])
|
123
|
-
v[fieldtable[o[0]]] = o[2]
|
124
|
-
|
125
|
-
next unless f == 1
|
126
|
-
permessage[fieldtable[o[0]]] = o[2]
|
127
|
-
end
|
128
|
-
else
|
129
|
-
# Continued line of the value of Diagnostic-Code field
|
130
|
-
next unless p.start_with?('Diagnostic-Code:')
|
131
|
-
next unless cv = e.match(/\A[ \t]+(.+)\z/)
|
132
|
-
v['diagnosis'] << ' ' << cv[1]
|
133
|
-
havepassed[-1] = 'Diagnostic-Code: ' << e
|
134
|
-
end
|
135
|
-
end # End of message/delivery-status
|
136
|
-
end
|
137
|
-
|
138
|
-
if recipients == 0 && mbody =~ /notificationType/
|
139
|
-
# Try to parse with Sisimai::Bite::JSON::AmazonSES module
|
140
|
-
require 'sisimai/bite/json/amazonses'
|
141
|
-
j = Sisimai::Bite::JSON::AmazonSES.scan(mhead, mbody)
|
142
|
-
|
143
|
-
if j['ds'].is_a? Array
|
144
|
-
# Update dscontents
|
145
|
-
dscontents = j['ds']
|
146
|
-
recipients = j['ds'].size
|
147
|
-
end
|
148
|
-
end
|
149
|
-
return nil unless recipients > 0
|
150
|
-
|
151
|
-
dscontents.each do |e|
|
152
|
-
# Set default values if each value is empty.
|
153
|
-
e['lhost'] ||= permessage['rhost']
|
154
|
-
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
155
|
-
|
156
|
-
e['agent'] = self.smtpagent
|
157
|
-
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].to_s.tr("\n", ' '))
|
158
|
-
|
159
|
-
if e['status'].to_s.start_with?('5.0.0', '5.1.0', '4.0.0', '4.1.0')
|
160
|
-
# Get other D.S.N. value from the error message
|
161
|
-
errormessage = e['diagnosis']
|
162
|
-
|
163
|
-
if cv = e['diagnosis'].match(/["'](\d[.]\d[.]\d.+)['"]/)
|
164
|
-
# 5.1.0 - Unknown address error 550-'5.7.1 ...
|
165
|
-
errormessage = cv[1]
|
166
|
-
end
|
167
|
-
e['status'] = Sisimai::SMTP::Status.find(errormessage) || e['status']
|
168
|
-
end
|
169
|
-
|
170
|
-
MessagesOf.each_key do |r|
|
171
|
-
# Verify each regular expression of session errors
|
172
|
-
next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
|
173
|
-
e['reason'] = r
|
174
|
-
break
|
175
|
-
end
|
176
|
-
|
177
|
-
end
|
178
|
-
|
179
|
-
rfc822part = Sisimai::RFC5322.weedout(rfc822list)
|
180
|
-
return { 'ds' => dscontents, 'rfc822' => rfc822part }
|
181
|
-
end
|
182
|
-
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
@@ -1,242 +0,0 @@
|
|
1
|
-
module Sisimai::Bite::JSON
|
2
|
-
# Sisimai::Bite::JSON::AmazonSES parses a bounce object(JSON) which created
|
3
|
-
# by Amazon Simple Email Service.
|
4
|
-
# Methods in the module are called from only Sisimai::Message.
|
5
|
-
module AmazonSES
|
6
|
-
# Imported from p5-Sisimail/lib/Sisimai/Bite/JSON/AmazonSES.pm
|
7
|
-
class << self
|
8
|
-
require 'sisimai/bite/json'
|
9
|
-
|
10
|
-
# https://docs.aws.amazon.com/en_us/ses/latest/DeveloperGuide/notification-contents.html
|
11
|
-
BounceType = {
|
12
|
-
'Permanent' => {
|
13
|
-
'General' => '',
|
14
|
-
'NoEmail' => '',
|
15
|
-
'Suppressed' => '',
|
16
|
-
},
|
17
|
-
'Transient' => {
|
18
|
-
'General' => '',
|
19
|
-
'MailboxFull' => 'mailboxfull',
|
20
|
-
'MessageTooLarge' => 'mesgtoobig',
|
21
|
-
'ContentRejected' => '',
|
22
|
-
'AttachmentRejected' => '',
|
23
|
-
},
|
24
|
-
}.freeze
|
25
|
-
|
26
|
-
def smtpagent; return Sisimai::Bite.smtpagent(self); end
|
27
|
-
def description; return 'Amazon SES(JSON): http://aws.amazon.com/ses/'; end
|
28
|
-
|
29
|
-
# Parse bounce messages from Amazon SES(JSON)
|
30
|
-
# @param [Hash] mhead Message headers of a bounce email
|
31
|
-
# @options mhead [String] from From header
|
32
|
-
# @options mhead [String] date Date header
|
33
|
-
# @options mhead [String] subject Subject header
|
34
|
-
# @options mhead [Array] received Received headers
|
35
|
-
# @options mhead [String] others Other required headers
|
36
|
-
# @param [String] mbody Message body of a bounce email
|
37
|
-
# @return [Hash, Nil] Bounce data list and message/rfc822
|
38
|
-
# part or nil if it failed to parse or
|
39
|
-
# the arguments are missing
|
40
|
-
# @since v4.20.0
|
41
|
-
def scan(mhead, mbody)
|
42
|
-
return nil unless mhead['x-amz-sns-message-id']
|
43
|
-
return nil if mhead['x-amz-sns-message-id'].empty?
|
44
|
-
|
45
|
-
hasdivided = mbody.split("\n")
|
46
|
-
jsonstring = ''
|
47
|
-
jsonthings = nil
|
48
|
-
foldedline = false
|
49
|
-
|
50
|
-
while e = hasdivided.shift do
|
51
|
-
# Find JSON string from the message body
|
52
|
-
next if e.empty?
|
53
|
-
break if e == '--'
|
54
|
-
break if e == '__END_OF_EMAIL_MESSAGE__'
|
55
|
-
|
56
|
-
# The line starts with " ", continued from !\n.
|
57
|
-
e.lstrip! if foldedline
|
58
|
-
foldedline = false
|
59
|
-
|
60
|
-
if e.end_with?('!')
|
61
|
-
# ... long long line ...![\n]
|
62
|
-
e.chomp!('!')
|
63
|
-
foldedline = true
|
64
|
-
end
|
65
|
-
jsonstring << e
|
66
|
-
end
|
67
|
-
|
68
|
-
begin
|
69
|
-
if RUBY_PLATFORM.start_with?('java')
|
70
|
-
# java-based ruby environment like JRuby.
|
71
|
-
require 'jrjackson'
|
72
|
-
jsonobject = JrJackson::Json.load(jsonstring)
|
73
|
-
|
74
|
-
# 'Message' => '{"notificationType":"Bounce",...
|
75
|
-
jsonthings = JrJackson::Json.load(jsonobject['Message']) if jsonobject['Message']
|
76
|
-
else
|
77
|
-
# Matz' Ruby Implementation
|
78
|
-
require 'oj'
|
79
|
-
jsonobject = Oj.load(jsonstring)
|
80
|
-
|
81
|
-
# 'Message' => '{"notificationType":"Bounce",...
|
82
|
-
jsonthings = Oj.load(jsonobject['Message']) if jsonobject['Message']
|
83
|
-
end
|
84
|
-
jsonthings ||= jsonobject
|
85
|
-
|
86
|
-
rescue StandardError => ce
|
87
|
-
# Something wrong in decoding JSON
|
88
|
-
warn ' ***warning: Failed to decode JSON: ' << ce.to_s
|
89
|
-
return nil
|
90
|
-
end
|
91
|
-
|
92
|
-
return adapt(jsonthings)
|
93
|
-
end
|
94
|
-
|
95
|
-
# @abstract Adapt Amazon SES bounce object for Sisimai::Message format
|
96
|
-
# @param [Hash] argvs bounce object(JSON) retrieved from Amazon SNS
|
97
|
-
# @return [Hash, Nil] Bounce data list and message/rfc822 part or
|
98
|
-
# nil if it failed to parse or the
|
99
|
-
# arguments are missing
|
100
|
-
# @since v4.20.0
|
101
|
-
def adapt(argvs)
|
102
|
-
return nil unless argvs.is_a? Hash
|
103
|
-
return nil if argvs.empty?
|
104
|
-
return nil unless argvs.key?('notificationType')
|
105
|
-
|
106
|
-
dscontents = [Sisimai::Bite.DELIVERYSTATUS]
|
107
|
-
rfc822head = {} # (Hash) Check flags for headers in RFC822 part
|
108
|
-
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
109
|
-
labeltable = {
|
110
|
-
'Bounce' => 'bouncedRecipients',
|
111
|
-
'Complaint' => 'complainedRecipients',
|
112
|
-
}
|
113
|
-
v = nil
|
114
|
-
|
115
|
-
if %w[Bounce Complaint].index(argvs['notificationType'])
|
116
|
-
# { "notificationType":"Bounce", "bounce": { "bounceType":"Permanent",...
|
117
|
-
o = argvs[argvs['notificationType'].downcase].dup
|
118
|
-
r = o[labeltable[argvs['notificationType']]] || []
|
119
|
-
|
120
|
-
while e = r.shift do
|
121
|
-
# 'bouncedRecipients' => [ { 'emailAddress' => 'bounce@si...' }, ... ]
|
122
|
-
# 'complainedRecipients' => [ { 'emailAddress' => 'complaint@si...' }, ... ]
|
123
|
-
next unless Sisimai::RFC5322.is_emailaddress(e['emailAddress'])
|
124
|
-
|
125
|
-
v = dscontents[-1]
|
126
|
-
if v['recipient']
|
127
|
-
# There are multiple recipient addresses in the message body.
|
128
|
-
dscontents << Sisimai::Bite.DELIVERYSTATUS
|
129
|
-
v = dscontents[-1]
|
130
|
-
end
|
131
|
-
recipients += 1
|
132
|
-
v['recipient'] = e['emailAddress']
|
133
|
-
|
134
|
-
if argvs['notificationType'] == 'Bounce'
|
135
|
-
# 'bouncedRecipients => [ {
|
136
|
-
# 'emailAddress' => 'bounce@simulator.amazonses.com',
|
137
|
-
# 'action' => 'failed',
|
138
|
-
# 'status' => '5.1.1',
|
139
|
-
# 'diagnosticCode' => 'smtp; 550 5.1.1 user unknown'
|
140
|
-
# }, ... ]
|
141
|
-
v['action'] = e['action']
|
142
|
-
v['status'] = e['status']
|
143
|
-
|
144
|
-
if cv = e['diagnosticCode'].match(/\A(.+?);[ ]*(.+)\z/)
|
145
|
-
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
146
|
-
v['spec'] = cv[1].upcase
|
147
|
-
v['diagnosis'] = cv[2]
|
148
|
-
else
|
149
|
-
v['diagnosis'] = e['diagnosticCode']
|
150
|
-
end
|
151
|
-
|
152
|
-
# 'reportingMTA' => 'dsn; a27-23.smtp-out.us-west-2.amazonses.com',
|
153
|
-
if cv = o['reportingMTA'].match(/\Adsn;[ ](.+)\z/) then v['lhost'] = cv[1] end
|
154
|
-
|
155
|
-
if BounceType.key?(o['bounceType']) &&
|
156
|
-
BounceType[o['bounceType']].key?(o['bounceSubType'])
|
157
|
-
# 'bounce' => {
|
158
|
-
# 'bounceType' => 'Permanent',
|
159
|
-
# 'bounceSubType' => 'General'
|
160
|
-
# },
|
161
|
-
v['reason'] = BounceType[o['bounceType']][o['bounceSubType']]
|
162
|
-
end
|
163
|
-
else
|
164
|
-
# 'complainedRecipients' => [ {
|
165
|
-
# 'emailAddress' => 'complaint@simulator.amazonses.com' }, ... ],
|
166
|
-
v['reason'] = 'feedback'
|
167
|
-
v['feedbacktype'] = o['complaintFeedbackType'] || ''
|
168
|
-
end
|
169
|
-
|
170
|
-
v['date'] = o['timestamp'] || argvs['mail']['timestamp']
|
171
|
-
v['date'].sub!(/[.]\d+Z\z/, '')
|
172
|
-
end
|
173
|
-
elsif argvs['notificationType'] == 'Delivery'
|
174
|
-
# { "notificationType":"Delivery", "delivery": { ...
|
175
|
-
o = argvs['delivery'].dup
|
176
|
-
r = o['recipients'] || []
|
177
|
-
|
178
|
-
while e = r.shift do
|
179
|
-
# 'delivery' => {
|
180
|
-
# 'timestamp' => '2016-11-23T12:01:03.512Z',
|
181
|
-
# 'processingTimeMillis' => 3982,
|
182
|
-
# 'reportingMTA' => 'a27-29.smtp-out.us-west-2.amazonses.com',
|
183
|
-
# 'recipients' => [
|
184
|
-
# 'success@simulator.amazonses.com'
|
185
|
-
# ],
|
186
|
-
# 'smtpResponse' => '250 2.6.0 Message received'
|
187
|
-
# },
|
188
|
-
next unless Sisimai::RFC5322.is_emailaddress(e)
|
189
|
-
|
190
|
-
v = dscontents[-1]
|
191
|
-
if v['recipient']
|
192
|
-
# There are multiple recipient addresses in the message body.
|
193
|
-
dscontents << Sisimai::Bite.DELIVERYSTATUS
|
194
|
-
v = dscontents[-1]
|
195
|
-
end
|
196
|
-
recipients += 1
|
197
|
-
v['recipient'] = e
|
198
|
-
v['lhost'] = o['reportingMTA'] || ''
|
199
|
-
v['diagnosis'] = o['smtpResponse'] || ''
|
200
|
-
v['status'] = Sisimai::SMTP::Status.find(v['diagnosis']) || ''
|
201
|
-
v['replycode'] = Sisimai::SMTP::Reply.find(v['diagnosis']) || ''
|
202
|
-
v['reason'] = 'delivered'
|
203
|
-
v['action'] = 'deliverable'
|
204
|
-
|
205
|
-
v['date'] = o['timestamp'] || argvs['mail']['timestamp']
|
206
|
-
v['date'].sub!(/[.]\d+Z\z/, '')
|
207
|
-
end
|
208
|
-
else
|
209
|
-
# The value of "notificationType" is not any of "Bounce", "Complaint",
|
210
|
-
# or "Delivery".
|
211
|
-
return nil
|
212
|
-
end
|
213
|
-
return nil unless recipients > 0
|
214
|
-
|
215
|
-
dscontents.each do |e|
|
216
|
-
e['agent'] = self.smtpagent
|
217
|
-
end
|
218
|
-
|
219
|
-
if argvs['mail']['headers']
|
220
|
-
# "headersTruncated":false,
|
221
|
-
# "headers":[ { ...
|
222
|
-
argvs['mail']['headers'].each do |e|
|
223
|
-
# 'headers' => [ { 'name' => 'From', 'value' => 'neko@nyaan.jp' }, ... ],
|
224
|
-
next unless %w[From To Subject Message-ID Date].index(e['name'])
|
225
|
-
rfc822head[e['name'].downcase] = e['value']
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
unless rfc822head['message-id']
|
230
|
-
# Try to get the value of "Message-Id".
|
231
|
-
if argvs['mail']['messageId']
|
232
|
-
# 'messageId' => '01010157e48f9b9b-891e9a0e-9c9d-4773-9bfe-608f2ef4756d-000000'
|
233
|
-
rfc822head['message-id'] = argvs['mail']['messageId']
|
234
|
-
end
|
235
|
-
end
|
236
|
-
return { 'ds' => dscontents, 'rfc822' => rfc822head }
|
237
|
-
end
|
238
|
-
|
239
|
-
end
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
@@ -1,123 +0,0 @@
|
|
1
|
-
module Sisimai::Bite::JSON
|
2
|
-
# Sisimai::Bite::JSON::SendGrid parses a bounce object(JSON) which created by
|
3
|
-
# SendGrid. Methods in the module are called from only Sisimai::Message.
|
4
|
-
module SendGrid
|
5
|
-
# Imported from p5-Sisimail/lib/Sisimai/Bite/JSON/SendGrid.pm
|
6
|
-
class << self
|
7
|
-
require 'sisimai/bite/json'
|
8
|
-
|
9
|
-
def smtpagent; return Sisimai::Bite.smtpagent(self); end
|
10
|
-
def description; return 'SendGrid(JSON): http://sendgrid.com/'; end
|
11
|
-
|
12
|
-
# @abstract Adapt SendGrid bounce object for Sisimai::Message format
|
13
|
-
# @param [Hash] argvs bounce object(JSON) retrieved from SendGrid API
|
14
|
-
# @return [Hash, Nil] Bounce data list and message/rfc822 part or
|
15
|
-
# nil if it failed to parse or the
|
16
|
-
# arguments are missing
|
17
|
-
# @since v4.20.0
|
18
|
-
def adapt(argvs)
|
19
|
-
return nil unless argvs.is_a? Hash
|
20
|
-
return nil if argvs.empty?
|
21
|
-
return nil unless argvs.key?('email')
|
22
|
-
return nil unless Sisimai::RFC5322.is_emailaddress(argvs['email'])
|
23
|
-
|
24
|
-
dscontents = nil
|
25
|
-
rfc822head = {}
|
26
|
-
v = nil
|
27
|
-
|
28
|
-
if argvs.key?('event')
|
29
|
-
# https://sendgrid.com/docs/API_Reference/Webhooks/event.html
|
30
|
-
# {
|
31
|
-
# 'tls' => 0,
|
32
|
-
# 'timestamp' => 1504555832,
|
33
|
-
# 'event' => 'bounce',
|
34
|
-
# 'email' => 'mailboxfull@example.jp',
|
35
|
-
# 'ip' => '192.0.2.22',
|
36
|
-
# 'sg_message_id' => '03_Wof6nRbqqzxRvLpZbfw.filter0017p3mdw1-11399-59ADB335-16.0',
|
37
|
-
# 'type' => 'blocked',
|
38
|
-
# 'sg_event_id' => 'S4wr46YHS0qr3BKhawTQjQ',
|
39
|
-
# 'reason' => '550 5.2.2 <mailboxfull@example.jp>... Mailbox Full ',
|
40
|
-
# 'smtp-id' => '<201709042010.v84KAQ5T032530@example.nyaan.jp>',
|
41
|
-
# 'status' => '5.2.2'
|
42
|
-
# },
|
43
|
-
return nil unless %w[bounce deferred delivered spamreport].include?(argvs['event'])
|
44
|
-
dscontents = [Sisimai::Bite.DELIVERYSTATUS]
|
45
|
-
diagnostic = argvs['reason'] || ''
|
46
|
-
diagnostic = argvs['response'] || '' if diagnostic.empty?
|
47
|
-
timestamp0 = Sisimai::Time.parse(::Time.at(argvs['timestamp']).to_s)
|
48
|
-
v = dscontents[-1]
|
49
|
-
|
50
|
-
v['date'] = timestamp0.strftime("%a, %d %b %Y %T %z")
|
51
|
-
v['agent'] = self.smtpagent
|
52
|
-
v['lhost'] = argvs['ip'] || ''
|
53
|
-
v['status'] = argvs['status'] || nil
|
54
|
-
v['diagnosis'] = Sisimai::String.sweep(diagnostic)
|
55
|
-
v['recipient'] = argvs['email']
|
56
|
-
|
57
|
-
if argvs['event'] == 'delivered'
|
58
|
-
# "event": "delivered"
|
59
|
-
v['reason'] = 'delivered'
|
60
|
-
elsif argvs['event'] == 'spamreport'
|
61
|
-
# [
|
62
|
-
# {
|
63
|
-
# "email": "kijitora@example.com",
|
64
|
-
# "timestamp": 1504837383,
|
65
|
-
# "sg_message_id": "6_hrAeKvTDaB5ynBI2nbnQ.filter0002p3las1-27574-59B1FDA3-19.0",
|
66
|
-
# "sg_event_id": "o70uHqbMSXOaaoveMZIjjg",
|
67
|
-
# "event": "spamreport"
|
68
|
-
# }
|
69
|
-
# ]
|
70
|
-
v['reason'] = 'feedback'
|
71
|
-
v['feedbacktype'] = 'abuse'
|
72
|
-
end
|
73
|
-
v['status'] ||= Sisimai::SMTP::Status.find(v['diagnosis']) || ''
|
74
|
-
v['replycode'] ||= Sisimai::SMTP::Reply.find(v['diagnosis']) || ''
|
75
|
-
|
76
|
-
# Generate pseudo message/rfc822 part
|
77
|
-
rfc822head = {
|
78
|
-
'from' => Sisimai::Address.undisclosed('s'),
|
79
|
-
'message-id' => v['sg_message_id'],
|
80
|
-
}
|
81
|
-
else
|
82
|
-
# {
|
83
|
-
# "status": "4.0.0",
|
84
|
-
# "created": "2011-09-16 22:02:19",
|
85
|
-
# "reason": "Unable to resolve MX host sendgrid.ne",
|
86
|
-
# "email": "esting@sendgrid.ne"
|
87
|
-
# },
|
88
|
-
dscontents = [Sisimai::Bite.DELIVERYSTATUS]
|
89
|
-
v = dscontents[-1]
|
90
|
-
|
91
|
-
v['recipient'] = argvs['email']
|
92
|
-
v['date'] = argvs['created'] || ''
|
93
|
-
|
94
|
-
statuscode = argvs['status'] || ''
|
95
|
-
diagnostic = Sisimai::String.sweep(argvs['reason']) || ''
|
96
|
-
|
97
|
-
if statuscode =~ /\A[245]\d\d\z/
|
98
|
-
# "status": "550"
|
99
|
-
v['replycode'] = statuscode
|
100
|
-
|
101
|
-
elsif statuscode =~ /\A[245][.]\d[.]\d+\z/
|
102
|
-
# "status": "5.1.1"
|
103
|
-
v['status'] = statuscode
|
104
|
-
end
|
105
|
-
|
106
|
-
v['status'] ||= Sisimai::SMTP::Status.find(diagnostic) || ''
|
107
|
-
v['replycode'] ||= Sisimai::SMTP::Reply.find(diagnostic) || ''
|
108
|
-
v['diagnosis'] = argvs['reason'] || ''
|
109
|
-
v['agent'] = self.smtpagent
|
110
|
-
|
111
|
-
# Generate pseudo message/rfc822 part
|
112
|
-
rfc822head = {
|
113
|
-
'from' => Sisimai::Address.undisclosed('s'),
|
114
|
-
'date' => v['date'],
|
115
|
-
}
|
116
|
-
end
|
117
|
-
return { 'ds' => dscontents, 'rfc822' => rfc822head }
|
118
|
-
end
|
119
|
-
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|