sisimai 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/rake-test.yml +1 -1
- data/ChangeLog.md +102 -0
- data/Makefile +4 -2
- data/README-JA.md +23 -16
- data/README.md +22 -15
- data/lib/sisimai/arf.rb +121 -210
- data/lib/sisimai/fact.rb +208 -158
- data/lib/sisimai/lda.rb +98 -0
- data/lib/sisimai/lhost/activehunter.rb +1 -1
- data/lib/sisimai/lhost/amazonses.rb +185 -301
- data/lib/sisimai/lhost/apachejames.rb +48 -51
- data/lib/sisimai/lhost/biglobe.rb +1 -2
- data/lib/sisimai/lhost/courier.rb +10 -8
- data/lib/sisimai/lhost/domino.rb +25 -25
- data/lib/sisimai/lhost/dragonfly.rb +3 -4
- data/lib/sisimai/lhost/einsundeins.rb +3 -4
- data/lib/sisimai/lhost/exchange2003.rb +6 -8
- data/lib/sisimai/lhost/exchange2007.rb +111 -101
- data/lib/sisimai/lhost/exim.rb +232 -242
- data/lib/sisimai/lhost/ezweb.rb +43 -51
- data/lib/sisimai/lhost/fml.rb +2 -3
- data/lib/sisimai/lhost/gmail.rb +32 -28
- data/lib/sisimai/lhost/gmx.rb +4 -16
- data/lib/sisimai/lhost/googlegroups.rb +9 -8
- data/lib/sisimai/lhost/googleworkspace.rb +94 -0
- data/lib/sisimai/lhost/imailserver.rb +7 -16
- data/lib/sisimai/lhost/interscanmss.rb +1 -1
- data/lib/sisimai/lhost/kddi.rb +3 -4
- data/lib/sisimai/lhost/mailfoundry.rb +2 -5
- data/lib/sisimai/lhost/mailmarshalsmtp.rb +1 -2
- data/lib/sisimai/lhost/messagingserver.rb +14 -13
- data/lib/sisimai/lhost/mfilter.rb +4 -3
- data/lib/sisimai/lhost/notes.rb +2 -4
- data/lib/sisimai/lhost/opensmtpd.rb +2 -2
- data/lib/sisimai/lhost/postfix.rb +25 -27
- data/lib/sisimai/lhost/qmail.rb +130 -106
- data/lib/sisimai/lhost/sendmail.rb +19 -18
- data/lib/sisimai/lhost/v5sendmail.rb +88 -60
- data/lib/sisimai/lhost/verizon.rb +2 -2
- data/lib/sisimai/lhost/x1.rb +1 -1
- data/lib/sisimai/lhost/x2.rb +1 -2
- data/lib/sisimai/lhost/x3.rb +2 -2
- data/lib/sisimai/lhost/x6.rb +1 -1
- data/lib/sisimai/lhost/zoho.rb +2 -2
- data/lib/sisimai/lhost.rb +18 -21
- data/lib/sisimai/message.rb +93 -146
- data/lib/sisimai/order.rb +21 -77
- data/lib/sisimai/reason/authfailure.rb +1 -4
- data/lib/sisimai/reason/badreputation.rb +2 -2
- data/lib/sisimai/reason/blocked.rb +7 -10
- data/lib/sisimai/reason/contenterror.rb +7 -1
- data/lib/sisimai/reason/exceedlimit.rb +1 -4
- data/lib/sisimai/reason/failedstarttls.rb +42 -0
- data/lib/sisimai/reason/filtered.rb +5 -4
- data/lib/sisimai/reason/hasmoved.rb +1 -2
- data/lib/sisimai/reason/hostunknown.rb +3 -3
- data/lib/sisimai/reason/mailboxfull.rb +2 -4
- data/lib/sisimai/reason/mailererror.rb +1 -2
- data/lib/sisimai/reason/mesgtoobig.rb +2 -4
- data/lib/sisimai/reason/norelaying.rb +2 -3
- data/lib/sisimai/reason/notaccept.rb +2 -3
- data/lib/sisimai/reason/notcompliantrfc.rb +10 -4
- data/lib/sisimai/reason/rejected.rb +1 -1
- data/lib/sisimai/reason/requireptr.rb +2 -2
- data/lib/sisimai/reason/securityerror.rb +1 -3
- data/lib/sisimai/reason/spamdetected.rb +6 -8
- data/lib/sisimai/reason/speeding.rb +1 -2
- data/lib/sisimai/reason/suppressed.rb +36 -0
- data/lib/sisimai/reason/suspend.rb +1 -3
- data/lib/sisimai/reason/systemerror.rb +5 -0
- data/lib/sisimai/reason/toomanyconn.rb +1 -2
- data/lib/sisimai/reason/userunknown.rb +1 -1
- data/lib/sisimai/reason/virusdetected.rb +5 -6
- data/lib/sisimai/reason.rb +77 -73
- data/lib/sisimai/rfc1123.rb +152 -0
- data/lib/sisimai/rfc1894.rb +102 -62
- data/lib/sisimai/rfc2045.rb +2 -1
- data/lib/sisimai/rfc3464/thirdparty.rb +102 -0
- data/lib/sisimai/rfc3464.rb +222 -343
- data/lib/sisimai/rfc3834.rb +1 -1
- data/lib/sisimai/rfc5322.rb +7 -17
- data/lib/sisimai/rfc791.rb +69 -0
- data/lib/sisimai/rhost/aol.rb +36 -0
- data/lib/sisimai/rhost/apple.rb +5 -2
- data/lib/sisimai/rhost/cox.rb +3 -2
- data/lib/sisimai/rhost/facebook.rb +100 -0
- data/lib/sisimai/rhost/franceptt.rb +3 -2
- data/lib/sisimai/rhost/godaddy.rb +3 -2
- data/lib/sisimai/rhost/google.rb +19 -17
- data/lib/sisimai/rhost/gsuite.rb +42 -0
- data/lib/sisimai/rhost/iua.rb +3 -3
- data/lib/sisimai/rhost/kddi.rb +3 -2
- data/lib/sisimai/rhost/messagelabs.rb +37 -0
- data/lib/sisimai/rhost/microsoft.rb +56 -49
- data/lib/sisimai/rhost/mimecast.rb +29 -27
- data/lib/sisimai/rhost/nttdocomo.rb +4 -3
- data/lib/sisimai/rhost/outlook.rb +36 -0
- data/lib/sisimai/rhost/spectrum.rb +3 -2
- data/lib/sisimai/rhost/tencent.rb +3 -2
- data/lib/sisimai/rhost/yahooinc.rb +4 -3
- data/lib/sisimai/rhost.rb +69 -39
- data/lib/sisimai/smtp/command.rb +31 -21
- data/lib/sisimai/smtp/failure.rb +103 -0
- data/lib/sisimai/smtp/reply.rb +29 -25
- data/lib/sisimai/smtp/status.rb +36 -19
- data/lib/sisimai/smtp/transcript.rb +15 -15
- data/lib/sisimai/string.rb +0 -46
- data/lib/sisimai/version.rb +1 -1
- data/set-of-emails/maildir/bsd/lhost-postfix-30.eml +81 -81
- data/set-of-emails/maildir/bsd/{lhost-aol-03.eml → rhost-aol-03.eml} +1264 -1264
- data/set-of-emails/maildir/bsd/{lhost-aol-04.eml → rhost-aol-04.eml} +1260 -1260
- data/set-of-emails/maildir/bsd/{lhost-aol-05.eml → rhost-aol-05.eml} +105 -105
- data/set-of-emails/maildir/bsd/{lhost-aol-06.eml → rhost-aol-06.eml} +105 -105
- data/set-of-emails/maildir/bsd/rhost-gsuite-01.eml +189 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-02.eml +180 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-03.eml +251 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-04.eml +211 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-05.eml +226 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-06.eml +257 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-07.eml +289 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-08.eml +231 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-09.eml +231 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-10.eml +254 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-11.eml +228 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-12.eml +271 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-13.eml +261 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-14.eml +273 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-15.eml +229 -0
- data/set-of-emails/maildir/bsd/{lhost-messagelabs-01.eml → rhost-messagelabs-01.eml} +93 -93
- data/set-of-emails/maildir/bsd/rhost-outlook-01.eml +72 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-02.eml +72 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-03.eml +72 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-04.eml +79 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-06.eml +75 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-07.eml +70 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-08.eml +70 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-09.eml +56 -0
- data/set-of-emails/maildir/tmp/arf-22.eml +49 -0
- data/set-of-emails/maildir/tmp/arf-23.eml +49 -0
- data/set-of-emails/maildir/tmp/arf-24.eml +50 -0
- data/set-of-emails/maildir/tmp/lhost-exim-07.eml +28 -0
- metadata +73 -56
- data/lib/sisimai/lhost/amavis.rb +0 -163
- data/lib/sisimai/lhost/amazonworkmail.rb +0 -127
- data/lib/sisimai/lhost/aol.rb +0 -125
- data/lib/sisimai/lhost/barracuda.rb +0 -92
- data/lib/sisimai/lhost/bigfoot.rb +0 -125
- data/lib/sisimai/lhost/facebook.rb +0 -188
- data/lib/sisimai/lhost/gsuite.rb +0 -194
- data/lib/sisimai/lhost/mailru.rb +0 -214
- data/lib/sisimai/lhost/mcafee.rb +0 -109
- data/lib/sisimai/lhost/messagelabs.rb +0 -120
- data/lib/sisimai/lhost/mxlogic.rb +0 -198
- data/lib/sisimai/lhost/office365.rb +0 -252
- data/lib/sisimai/lhost/outlook.rb +0 -129
- data/lib/sisimai/lhost/powermta.rb +0 -118
- data/lib/sisimai/lhost/receivingses.rb +0 -126
- data/lib/sisimai/lhost/sendgrid.rb +0 -150
- data/lib/sisimai/lhost/surfcontrol.rb +0 -105
- data/lib/sisimai/lhost/x4.rb +0 -269
- data/lib/sisimai/lhost/x5.rb +0 -112
- data/lib/sisimai/lhost/yahoo.rb +0 -102
- data/lib/sisimai/lhost/yandex.rb +0 -118
- data/lib/sisimai/mda.rb +0 -121
- data/lib/sisimai/smtp/error.rb +0 -119
- /data/set-of-emails/maildir/bsd/{lhost-googlegroups-15.eml → lhost-googleworkspace-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-x4-08.eml → lhost-x2-06.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-01.eml → rfc3464-51.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-03.eml → rfc3464-52.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-04.eml → rfc3464-53.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-05.eml → rfc3464-54.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-06.eml → rfc3464-55.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-07.eml → rfc3464-56.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-08.eml → rfc3464-57.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-09.eml → rfc3464-58.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-10.eml → rfc3464-59.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-11.eml → rfc3464-60.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-12.eml → rfc3464-61.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-13.eml → rfc3464-62.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-14.eml → rfc3464-63.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-15.eml → rfc3464-64.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-02.eml → rfc3464-65.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-aol-01.eml → rhost-aol-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-aol-02.eml → rhost-aol-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-facebook-03.eml → rhost-facebook-03.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-facebook-04.eml → rhost-facebook-04.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-messagelabs-02.eml → rhost-messagelabs-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-messagelabs-03.eml → rhost-messagelabs-03.eml} +0 -0
- /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-37.eml +0 -0
- /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-38.eml +0 -0
- /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-39.eml +0 -0
data/lib/sisimai/lda.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
module Sisimai
|
2
|
+
# Sisimai::LDA - Error message decoder for LDA
|
3
|
+
module LDA
|
4
|
+
class << self
|
5
|
+
LocalAgent = {
|
6
|
+
# Each error message should be a lower-cased string
|
7
|
+
# dovecot/src/deliver/deliver.c
|
8
|
+
# 11: #define DEFAULT_MAIL_REJECTION_HUMAN_REASON \
|
9
|
+
# 12: "Your message to <%t> was automatically rejected:%n%r"
|
10
|
+
"dovecot" => ["Your message to <", "> was automatically rejected:"],
|
11
|
+
"mail.local" => ["mail.local: "],
|
12
|
+
"procmail" => ["procmail: ", "/procmail "],
|
13
|
+
"maildrop" => ["maildrop: "],
|
14
|
+
"vpopmail" => ["vdelivermail: "],
|
15
|
+
"vmailmgr" => ["vdeliver: "],
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
MessagesOf = {
|
19
|
+
# Each error message should be a lower-cased string
|
20
|
+
"dovecot" => {
|
21
|
+
# dovecot/src/deliver/mail-send.c:94
|
22
|
+
"mailboxfull" => [
|
23
|
+
"not enough disk space",
|
24
|
+
"quota exceeded", # Dovecot 1.2 dovecot/src/plugins/quota/quota.c
|
25
|
+
"quota exceeded (mailbox for user is full)", # dovecot/src/plugins/quota/quota.c
|
26
|
+
],
|
27
|
+
"userunknown" => ["mailbox doesn't exist: "],
|
28
|
+
},
|
29
|
+
"mail.local" => {
|
30
|
+
"mailboxfull" => [
|
31
|
+
"disc quota exceeded",
|
32
|
+
"mailbox full or quota exceeded",
|
33
|
+
],
|
34
|
+
"systemerror" => ["temporary file write error"],
|
35
|
+
"userunknown" => [
|
36
|
+
": invalid mailbox path",
|
37
|
+
": unknown user:",
|
38
|
+
": user missing home directory",
|
39
|
+
": user unknown",
|
40
|
+
],
|
41
|
+
},
|
42
|
+
"procmail" => {
|
43
|
+
"mailboxfull" => ["quota exceeded while writing", "user over quota"],
|
44
|
+
"systemerror" => ["service unavailable"],
|
45
|
+
"systemfull" => ["no space left to finish writing"],
|
46
|
+
},
|
47
|
+
"maildrop" => {
|
48
|
+
"userunknown" => ["cannot find system user", "invalid user specified."],
|
49
|
+
"mailboxfull" => ["maildir over quota."],
|
50
|
+
},
|
51
|
+
"vpopmail" => {
|
52
|
+
"filtered" => ["user does not exist, but will deliver to "],
|
53
|
+
"mailboxfull" => ["domain is over quota", "user is over quota"],
|
54
|
+
"suspend" => ["account is locked email bounced"],
|
55
|
+
"userunknown" => ["sorry, no mailbox here by that name."],
|
56
|
+
},
|
57
|
+
"vmailmgr" => {
|
58
|
+
"mailboxfull" => ["delivery failed due to system quota violation"],
|
59
|
+
"userunknown" => [
|
60
|
+
"invalid or unknown base user or domain",
|
61
|
+
"invalid or unknown virtual user",
|
62
|
+
"user name does not refer to a virtual user",
|
63
|
+
],
|
64
|
+
},
|
65
|
+
}.freeze
|
66
|
+
|
67
|
+
# @abstract Decodes the message body and return the LDA name, the reason, and the error message
|
68
|
+
# @param [Sisimai::Fact] argvs Decoded email object
|
69
|
+
# @return [String] Bounce reason
|
70
|
+
def find(argvs)
|
71
|
+
return nil if argvs.nil?
|
72
|
+
return "" if argvs["diagnosticcode"].empty?
|
73
|
+
return "" if argvs["command"] != "" && argvs["command"] != "DATA"
|
74
|
+
|
75
|
+
deliversby = "" # [String] Local Delivery Agent name
|
76
|
+
reasontext = "" # [String] Error reason
|
77
|
+
issuedcode = argvs["diagnosticcode"].downcase
|
78
|
+
|
79
|
+
LocalAgent.each_key do |e|
|
80
|
+
# Find a lcoal delivery agent name from the entire message body
|
81
|
+
next unless LocalAgent[e].any? { |a| issuedcode.include?(a) }
|
82
|
+
deliversby = e; break
|
83
|
+
end
|
84
|
+
return "" if deliversby.empty?
|
85
|
+
|
86
|
+
MessagesOf[deliversby].each_key do |e|
|
87
|
+
# The key is a bounce reason name
|
88
|
+
next unless MessagesOf[deliversby][e].any? { |a| issuedcode.include?(a) }
|
89
|
+
reasontext = e; break
|
90
|
+
end
|
91
|
+
|
92
|
+
reasontext = "mailererror" if reasontext.empty?
|
93
|
+
return reasontext
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
@@ -48,7 +48,7 @@ module Sisimai::Lhost
|
|
48
48
|
|
49
49
|
if e.start_with?('>>> ') && e.index('@') > 1
|
50
50
|
# >>> kijitora@example.org <kijitora@example.org>
|
51
|
-
if v[
|
51
|
+
if v["recipient"] != ""
|
52
52
|
# There are multiple recipient addresses in the message body.
|
53
53
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
54
54
|
v = dscontents[-1]
|
@@ -2,16 +2,57 @@ module Sisimai::Lhost
|
|
2
2
|
# Sisimai::Lhost::AmazonSES decodes a bounce email which created by Amazon Simple Email Service
|
3
3
|
# https://aws.amazon.com/ses/. Methods in the module are called from only Sisimai::Message.
|
4
4
|
module AmazonSES
|
5
|
+
# ---------------------------------------------------------------------------------------------
|
6
|
+
# "notificationType": "Bounce"
|
7
|
+
# https://docs.aws.amazon.com/ses/latest/dg/notification-contents.html#bounce-object
|
8
|
+
#
|
9
|
+
# Bounce types
|
10
|
+
# The bounce object contains a bounce type of Undetermined, Permanent, or Transient. The
|
11
|
+
# Permanent and Transient bounce types can also contain one of several bounce subtypes.
|
12
|
+
#
|
13
|
+
# When you receive a bounce notification with a bounce type of Transient, you might be
|
14
|
+
# able to send email to that recipient in the future if the issue that caused the message
|
15
|
+
# to bounce is resolved.
|
16
|
+
#
|
17
|
+
# When you receive a bounce notification with a bounce type of Permanent, it's unlikely
|
18
|
+
# that you'll be able to send email to that recipient in the future. For this reason, you
|
19
|
+
# should immediately remove the recipient whose address produced the bounce from your
|
20
|
+
# mailing lists.
|
21
|
+
#
|
22
|
+
# "bounceType"/"bounceSubType" "Desription"
|
23
|
+
# Undetermined/Undetermined -- The bounce message didn't contain enough information for
|
24
|
+
# Amazon SES to determine the reason for the bounce.
|
25
|
+
#
|
26
|
+
# Permanent/General ---------- When you receive this type of bounce notification, you should
|
27
|
+
# immediately remove the recipient's email address from your
|
28
|
+
# mailing list.
|
29
|
+
# Permanent/NoEmail ---------- It was not possible to retrieve the recipient email address
|
30
|
+
# from the bounce message.
|
31
|
+
# Permanent/Suppressed ------- The recipient's email address is on the Amazon SES suppression
|
32
|
+
# list because it has a recent history of producing hard bounces.
|
33
|
+
# Permanent/OnAccountSuppressionList
|
34
|
+
# Amazon SES has suppressed sending to this address because it
|
35
|
+
# is on the account-level suppression list.
|
36
|
+
#
|
37
|
+
# Transient/General ---------- You might be able to send a message to the same recipient
|
38
|
+
# in the future if the issue that caused the message to bounce
|
39
|
+
# is resolved.
|
40
|
+
# Transient/MailboxFull ------ the recipient's inbox was full.
|
41
|
+
# Transient/MessageTooLarge -- message you sent was too large
|
42
|
+
# Transient/ContentRejected -- message you sent contains content that the provider doesn't allow
|
43
|
+
# Transient/AttachmentRejected the message contained an unacceptable attachment
|
5
44
|
class << self
|
6
45
|
require 'sisimai/lhost'
|
7
46
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
47
|
+
ReasonPair = {
|
48
|
+
"Supressed" => "suppressed",
|
49
|
+
"OnAccountSuppressionList" => "suppressed",
|
50
|
+
"General" => "onhold",
|
51
|
+
"MailboxFull" => "mailboxfull",
|
52
|
+
"MessageTooLarge" => "mesgtoobig",
|
53
|
+
"ContentRejected" => "contenterror",
|
54
|
+
"AttachmentRejected" => "securityerror",
|
13
55
|
}.freeze
|
14
|
-
MessagesOf = { 'expired' => ['Delivery expired'] }.freeze
|
15
56
|
|
16
57
|
# @abstract Decodes the bounce message from Amazon SES
|
17
58
|
# @param [Hash] mhead Message headers of a bounce email
|
@@ -19,317 +60,160 @@ module Sisimai::Lhost
|
|
19
60
|
# @return [Hash] Bounce data list and message/rfc822 part
|
20
61
|
# @return [Nil] it failed to decode or the arguments are missing
|
21
62
|
def inquire(mhead, mbody)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
# The line starts with " ", continued from !\n.
|
52
|
-
e.lstrip! if foldedline
|
53
|
-
foldedline = false
|
54
|
-
|
55
|
-
if e.end_with?('!')
|
56
|
-
# ... long long line ...![\n]
|
57
|
-
e.chomp!('!')
|
58
|
-
foldedline = true
|
59
|
-
end
|
60
|
-
jsonstring << e
|
61
|
-
end
|
62
|
-
|
63
|
-
begin
|
64
|
-
if RUBY_PLATFORM.start_with?('java')
|
65
|
-
# java-based ruby environment like JRuby.
|
66
|
-
require 'jrjackson'
|
67
|
-
jsonobject = JrJackson::Json.load(jsonstring)
|
68
|
-
|
69
|
-
# 'Message' => '{"notificationType":"Bounce",...
|
70
|
-
sespayload = JrJackson::Json.load(jsonobject['Message']) if jsonobject['Message']
|
71
|
-
else
|
72
|
-
# Matz' Ruby Implementation
|
73
|
-
require 'oj'
|
74
|
-
jsonobject = Oj.load(jsonstring)
|
75
|
-
|
76
|
-
# 'Message' => '{"notificationType":"Bounce",...
|
77
|
-
sespayload = Oj.load(jsonobject['Message']) if jsonobject['Message']
|
78
|
-
end
|
79
|
-
sespayload ||= jsonobject
|
80
|
-
|
81
|
-
rescue StandardError => ce
|
82
|
-
# Something wrong in decoding JSON
|
83
|
-
warn ' ***warning: Failed to decode JSON: ' << ce.to_s
|
84
|
-
return nil
|
63
|
+
return nil if mbody.include?("{") == false
|
64
|
+
return nil if mhead.has_key?("x-amz-sns-message-id") == false
|
65
|
+
return nil if mhead["x-amz-sns-message-id"].empty?
|
66
|
+
|
67
|
+
proceedsto = false
|
68
|
+
sespayload = mbody
|
69
|
+
while true
|
70
|
+
# Remote the following string begins with "--"
|
71
|
+
# --
|
72
|
+
# If you wish to stop receiving notifications from this topic, please click or visit the link below to unsubscribe:
|
73
|
+
# https://sns.us-west-2.amazonaws.com/unsubscribe.html?SubscriptionArn=arn:aws:sns:us-west-2:1...
|
74
|
+
p1 = mbody.index("\n\n--\n")
|
75
|
+
sespayload = mbody[0, p1] if p1
|
76
|
+
sespayload = sespayload.gsub("!\n ", "")
|
77
|
+
p2 = sespayload.index('"Message"')
|
78
|
+
|
79
|
+
if p2
|
80
|
+
# The JSON included in the email is a format like the following:
|
81
|
+
# {
|
82
|
+
# "Type" : "Notification",
|
83
|
+
# "MessageId" : "02f86d9b-eecf-573d-b47d-3d1850750c30",
|
84
|
+
# "TopicArn" : "arn:aws:sns:us-west-2:123456789012:SES-EJ-B",
|
85
|
+
# "Message" : "{\"notificationType\"...
|
86
|
+
sespayload = sespayload.gsub("\\", "")
|
87
|
+
p3 = sespayload.index("{", p2 + 9)
|
88
|
+
p4 = sespayload.index("\n", p2 + 9)
|
89
|
+
sespayload = sespayload[p3, p4 - p3]
|
90
|
+
sespayload = sespayload.chop if sespayload[-1, 1] == ","
|
91
|
+
sespayload = sespayload.chop if sespayload[-1, 1] == '"'
|
85
92
|
end
|
86
93
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
while e = r.shift do
|
101
|
-
# 'bouncedRecipients' => [ { 'emailAddress' => 'bounce@si...' }, ... ]
|
102
|
-
# 'complainedRecipients' => [ { 'emailAddress' => 'complaint@si...' }, ... ]
|
103
|
-
next unless Sisimai::Address.is_emailaddress(e['emailAddress'])
|
104
|
-
|
105
|
-
v = dscontents[-1]
|
106
|
-
if v['recipient']
|
107
|
-
# There are multiple recipient addresses in the message body.
|
108
|
-
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
109
|
-
v = dscontents[-1]
|
110
|
-
end
|
111
|
-
recipients += 1
|
112
|
-
v['recipient'] = e['emailAddress']
|
113
|
-
|
114
|
-
if p['notificationType'] == 'Bounce'
|
115
|
-
# 'bouncedRecipients => [ {
|
116
|
-
# 'emailAddress' => 'bounce@simulator.amazonses.com',
|
117
|
-
# 'action' => 'failed',
|
118
|
-
# 'status' => '5.1.1',
|
119
|
-
# 'diagnosticCode' => 'smtp; 550 5.1.1 user unknown'
|
120
|
-
# }, ... ]
|
121
|
-
v['action'] = e['action']
|
122
|
-
v['status'] = e['status']
|
123
|
-
|
124
|
-
if p0 = e['diagnosticCode'].index(';') || -1; p0 > 3
|
125
|
-
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
126
|
-
v['spec'] = e['diagnosticCode'][0, p0].upcase
|
127
|
-
v['diagnosis'] = e['diagnosticCode'][p0 + 2, e['diagnosticCode'].size]
|
128
|
-
else
|
129
|
-
v['diagnosis'] = e['diagnosticCode']
|
130
|
-
end
|
131
|
-
|
132
|
-
# 'reportingMTA' => 'dsn; a27-23.smtp-out.us-west-2.amazonses.com',
|
133
|
-
p0 = o['reportingMTA'].index('dsn; ')
|
134
|
-
v['lhost'] = Sisimai::String.sweep(o['reportingMTA'][p0 + 5, o['reportingMTA'].size]) if p0
|
135
|
-
|
136
|
-
if bouncetype[o['bounceType']] && bouncetype[o['bounceType']][o['bounceSubType']]
|
137
|
-
# 'bounce' => {
|
138
|
-
# 'bounceType' => 'Permanent',
|
139
|
-
# 'bounceSubType' => 'General'
|
140
|
-
# },
|
141
|
-
v['reason'] = bouncetype[o['bounceType']][o['bounceSubType']]
|
142
|
-
end
|
143
|
-
else
|
144
|
-
# 'complainedRecipients' => [ {
|
145
|
-
# 'emailAddress' => 'complaint@simulator.amazonses.com' }, ... ],
|
146
|
-
v['reason'] = 'feedback'
|
147
|
-
v['feedbacktype'] = o['complaintFeedbackType'] || ''
|
148
|
-
end
|
149
|
-
|
150
|
-
v['date'] = o['timestamp'] || p['mail']['timestamp']
|
151
|
-
v['date'].sub!(/[.]\d+Z\z/, '')
|
152
|
-
end
|
153
|
-
elsif p['notificationType'] == 'Delivery'
|
154
|
-
# { "notificationType":"Delivery", "delivery": { ...
|
155
|
-
o = p['delivery'].dup
|
156
|
-
r = o['recipients'] || []
|
157
|
-
|
158
|
-
while e = r.shift do
|
159
|
-
# 'delivery' => {
|
160
|
-
# 'timestamp' => '2016-11-23T12:01:03.512Z',
|
161
|
-
# 'processingTimeMillis' => 3982,
|
162
|
-
# 'reportingMTA' => 'a27-29.smtp-out.us-west-2.amazonses.com',
|
163
|
-
# 'recipients' => [
|
164
|
-
# 'success@simulator.amazonses.com'
|
165
|
-
# ],
|
166
|
-
# 'smtpResponse' => '250 2.6.0 Message received'
|
167
|
-
# },
|
168
|
-
next unless Sisimai::Address.is_emailaddress(e)
|
169
|
-
|
170
|
-
v = dscontents[-1]
|
171
|
-
if v['recipient']
|
172
|
-
# There are multiple recipient addresses in the message body.
|
173
|
-
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
174
|
-
v = dscontents[-1]
|
175
|
-
end
|
176
|
-
recipients += 1
|
177
|
-
v['recipient'] = e
|
178
|
-
v['lhost'] = o['reportingMTA'] || ''
|
179
|
-
v['diagnosis'] = o['smtpResponse'] || ''
|
180
|
-
v['status'] = Sisimai::SMTP::Status.find(v['diagnosis']) || ''
|
181
|
-
v['reason'] = 'delivered'
|
182
|
-
v['action'] = 'delivered'
|
183
|
-
|
184
|
-
v['date'] = o['timestamp'] || p['mail']['timestamp']
|
185
|
-
v['date'].sub!(/[.]\d+Z\z/, '')
|
186
|
-
end
|
94
|
+
break if sespayload.include?("notificationType") == false
|
95
|
+
break if sespayload.start_with?("{") == false
|
96
|
+
break if sespayload.end_with?("}") == false
|
97
|
+
proceedsto = true; break
|
98
|
+
end
|
99
|
+
return nil if proceedsto == false
|
100
|
+
|
101
|
+
jsonobject = nil
|
102
|
+
begin
|
103
|
+
if RUBY_PLATFORM.start_with?("java")
|
104
|
+
# java-based ruby environment like JRuby.
|
105
|
+
require "jrjackson"
|
106
|
+
jsonobject = JrJackson::Json.load(sespayload)
|
187
107
|
else
|
188
|
-
#
|
189
|
-
|
108
|
+
# Matz' Ruby Implementation
|
109
|
+
require "oj"
|
110
|
+
jsonobject = Oj.load(sespayload)
|
190
111
|
end
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
112
|
+
rescue StandardError => ce
|
113
|
+
# Something wrong in decoding JSON
|
114
|
+
warn ' ***warning: Failed to decode JSON: ' << ce.to_s
|
115
|
+
return nil
|
116
|
+
end
|
117
|
+
return nil if jsonobject.has_key?("notificationType") == false
|
118
|
+
|
119
|
+
require "sisimai/string"
|
120
|
+
require "sisimai/rfc1123"
|
121
|
+
require "sisimai/smtp/reply"
|
122
|
+
require "sisimai/smtp/status"
|
123
|
+
require "sisimai/smtp/command"
|
124
|
+
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
125
|
+
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
126
|
+
whatnotify = jsonobject["notificationType"][0, 1] || ""
|
127
|
+
v = dscontents[-1]
|
128
|
+
|
129
|
+
if whatnotify == "B"
|
130
|
+
# "notificationType":"Bounce"
|
131
|
+
p = jsonobject["bounce"]
|
132
|
+
r = p["bounceType"] == "Permanent" ? "5" : "4"
|
133
|
+
|
134
|
+
p["bouncedRecipients"].each do |e|
|
135
|
+
# {"emailAddress":"neko@example.jp", "action":"failed", "status":"5.1.1", "diagnosticCode": "..."}
|
136
|
+
if v["recipient"] != ""
|
137
|
+
# There are multiple recipient addresses in the message body.
|
138
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
139
|
+
v = dscontents[-1]
|
200
140
|
end
|
141
|
+
v["recipient"] = e["emailAddress"]
|
142
|
+
v["diagnosis"] = Sisimai::String.sweep(e["diagnosticCode"])
|
143
|
+
v["command"] = Sisimai::SMTP::Command.find(v["diagnosis"])
|
144
|
+
v["action"] = e["action"]
|
145
|
+
v["status"] = Sisimai::SMTP::Status.find(v["diagnosis"], r)
|
146
|
+
v["replycode"] = Sisimai::SMTP::Reply.find(v["diagnosis"], v["status"])
|
147
|
+
v["date"] = p["timestamp"]
|
148
|
+
v["lhost"] = Sisimai::RFC1123.find(p["reportingMTA"])
|
149
|
+
recipients += 1
|
201
150
|
end
|
202
151
|
|
203
|
-
|
204
|
-
#
|
205
|
-
|
206
|
-
|
207
|
-
rfc822head['message-id'] = p['mail']['messageId']
|
208
|
-
end
|
152
|
+
ReasonPair.each_key do |f|
|
153
|
+
# Try to find the bounce reason by "bounceSubType"
|
154
|
+
next unless ReasonPair[f] == p["bounceSubType"]
|
155
|
+
v["reason"] = f; break
|
209
156
|
end
|
210
|
-
return { 'ds' => dscontents, 'rfc822' => rfc822head }
|
211
157
|
|
212
|
-
|
213
|
-
#
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
# X-AWS-Outgoing: 199.255.192.156
|
221
|
-
# X-SES-Outgoing: 2016.10.12-54.240.27.6
|
222
|
-
match = 0
|
223
|
-
match += 1 if mhead['x-aws-outgoing']
|
224
|
-
match += 1 if mhead['x-ses-outgoing']
|
225
|
-
return nil unless match > 0
|
226
|
-
|
227
|
-
require 'sisimai/rfc1894'
|
228
|
-
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
229
|
-
permessage = {} # (Hash) Store values of each Per-Message field
|
230
|
-
|
231
|
-
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
232
|
-
bodyslices = emailparts[0].split("\n")
|
233
|
-
readslices = ['']
|
234
|
-
readcursor = 0 # (Integer) Points the current cursor position
|
235
|
-
v = nil
|
236
|
-
|
237
|
-
while e = bodyslices.shift do
|
238
|
-
# Read error messages and delivery status lines from the head of the email to the previous
|
239
|
-
# line of the beginning of the original message.
|
240
|
-
readslices << e # Save the current line for the next loop
|
241
|
-
|
242
|
-
if readcursor == 0
|
243
|
-
# Beginning of the bounce message or message/delivery-status part
|
244
|
-
if e.start_with?(StartingOf[:message][0], StartingOf[:message][1])
|
245
|
-
readcursor |= Indicators[:deliverystatus]
|
246
|
-
next
|
247
|
-
end
|
248
|
-
end
|
249
|
-
next if (readcursor & Indicators[:deliverystatus]) == 0
|
250
|
-
next if e.empty?
|
251
|
-
|
252
|
-
if f = Sisimai::RFC1894.match(e)
|
253
|
-
# "e" matched with any field defined in RFC3464
|
254
|
-
next unless o = Sisimai::RFC1894.field(e)
|
158
|
+
elsif whatnotify == "C"
|
159
|
+
# "notificationType":"Complaint"
|
160
|
+
p = jsonobject["complaint"]
|
161
|
+
p["complainedRecipients"].each do |e|
|
162
|
+
# {"emailAddress":"neko@example.jp"}
|
163
|
+
if v["recipient"] != ""
|
164
|
+
# There are multiple recipient addresses in the message body.
|
165
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
255
166
|
v = dscontents[-1]
|
256
|
-
|
257
|
-
if o[-1] == 'addr'
|
258
|
-
# Final-Recipient: rfc822; kijitora@example.jp
|
259
|
-
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
260
|
-
if o[0] == 'final-recipient'
|
261
|
-
# Final-Recipient: rfc822; kijitora@example.jp
|
262
|
-
if v['recipient']
|
263
|
-
# There are multiple recipient addresses in the message body.
|
264
|
-
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
265
|
-
v = dscontents[-1]
|
266
|
-
end
|
267
|
-
v['recipient'] = o[2]
|
268
|
-
recipients += 1
|
269
|
-
else
|
270
|
-
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
271
|
-
v['alias'] = o[2]
|
272
|
-
end
|
273
|
-
elsif o[-1] == 'code'
|
274
|
-
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
275
|
-
v['spec'] = o[1]
|
276
|
-
v['diagnosis'] = o[2]
|
277
|
-
else
|
278
|
-
# Other DSN fields defined in RFC3464
|
279
|
-
next unless fieldtable[o[0]]
|
280
|
-
v[fieldtable[o[0]]] = o[2]
|
281
|
-
|
282
|
-
next unless f
|
283
|
-
permessage[fieldtable[o[0]]] = o[2]
|
284
|
-
end
|
285
|
-
else
|
286
|
-
# Continued line of the value of Diagnostic-Code field
|
287
|
-
next unless readslices[-2].start_with?('Diagnostic-Code:')
|
288
|
-
next unless e.start_with?(' ')
|
289
|
-
v['diagnosis'] << ' ' << e
|
290
|
-
readslices[-1] = 'Diagnostic-Code: ' << e
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
if recipients == 0 && mbody =~ /notificationType/
|
295
|
-
# Try to decode with Sisimai::Lhost::AmazonSES module
|
296
|
-
j = Sisimai::Lhost::AmazonSES.json(mhead, mbody)
|
297
|
-
|
298
|
-
if j['ds'].is_a? Array
|
299
|
-
# Update dscontents
|
300
|
-
dscontents = j['ds']
|
301
|
-
recipients = j['ds'].size
|
302
167
|
end
|
168
|
+
v["recipient"] = e["emailAddress"]
|
169
|
+
v["reason"] = "feedback"
|
170
|
+
v["feedbacktype"] = p["complaintFeedbackType"]
|
171
|
+
v["date"] = p["timestamp"]
|
172
|
+
v["diagnosis"] = sprintf('"feedbackid":"%s", "useragent":"%s"}', p["feedbackId"], p["userAgent"])
|
173
|
+
recipients += 1
|
303
174
|
end
|
304
|
-
return nil unless recipients > 0
|
305
175
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
if
|
312
|
-
#
|
313
|
-
|
314
|
-
|
315
|
-
p1 = e['diagnosis'].index("-'"); p1 = e['diagnosis'].index('-"') unless p1
|
316
|
-
p2 = e['diagnosis'].rindex("' "); p2 = e['diagnosis'].rindex('" ') unless p2
|
317
|
-
errormessage = e['diagnosis'][p1 + 2, p2 - p1 - 2] if p1 && p2
|
318
|
-
e['status'] = Sisimai::SMTP::Status.find(errormessage) || e['status']
|
319
|
-
end
|
320
|
-
e['replycode'] ||= Sisimai::SMTP::Reply.find(e['diagnosis'], e['status'])
|
321
|
-
|
322
|
-
MessagesOf.each_key do |r|
|
323
|
-
# Verify each regular expression of session errors
|
324
|
-
next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
|
325
|
-
e['reason'] = r
|
326
|
-
break
|
176
|
+
elsif whatnotify == "D"
|
177
|
+
# "notificationType":"Delivery"
|
178
|
+
p = jsonobject["delivery"]
|
179
|
+
p["recipients"].each do |e|
|
180
|
+
# {"recipients":["neko@example.jp"]}
|
181
|
+
if v["recipient"] != ""
|
182
|
+
# There are multiple recipient addresses in the message body.
|
183
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
184
|
+
v = dscontents[-1]
|
327
185
|
end
|
186
|
+
v["recipient"] = e
|
187
|
+
v["reason"] = "delivered"
|
188
|
+
v["action"] = "delivered"
|
189
|
+
v["date"] = p["timestamp"]
|
190
|
+
v["lhost"] = Sisimai::RFC1123.find(p["reportingMTA"])
|
191
|
+
v["diagnosis"] = Sisimai::String.sweep(p["smtpResponse"])
|
192
|
+
v["command"] = Sisimai::SMTP::Command.find(v["diagnosis"])
|
193
|
+
v["status"] = Sisimai::SMTP::Status.find(v["diagnosis"], "2")
|
194
|
+
v["replycode"] = Sisimai::SMTP::Reply.find(v["diagnosis"], "2")
|
195
|
+
recipients += 1
|
328
196
|
end
|
329
197
|
|
330
|
-
|
331
|
-
|
332
|
-
|
198
|
+
else
|
199
|
+
# Unknown "notificationType" value
|
200
|
+
warn sprintf(" ***warning: There is no notificationType field or unknown type of notificationType field")
|
201
|
+
return nil
|
202
|
+
end
|
203
|
+
return nil if recipients == 0
|
204
|
+
|
205
|
+
# Date::Time.strptime() cannot parse "2016-11-25T01:49:01.000Z" format
|
206
|
+
dscontents.each { |e| e["date"] = e["date"].sub("T", " ").sub(/[.]\d{3}Z/, "") }
|
207
|
+
|
208
|
+
cv = ""
|
209
|
+
jsonobject["mail"]["headers"].each do |e|
|
210
|
+
cv << sprintf("%s: %s\n", e["name"], e["value"])
|
211
|
+
end
|
212
|
+
%w[date subject].each do |e|
|
213
|
+
next if jsonobject["mail"]["commonHeaders"].has_key?(e) == false
|
214
|
+
cv << sprintf("%s: %s\n", e.capitalize, jsonobject["mail"]["commonHeaders"][e])
|
215
|
+
end
|
216
|
+
return { "ds" => dscontents, "rfc822" => cv }
|
333
217
|
end
|
334
218
|
def description; return 'Amazon SES(Sending): https://aws.amazon.com/ses/'; end
|
335
219
|
end
|