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/rfc3464.rb
CHANGED
@@ -3,400 +3,279 @@ module Sisimai
|
|
3
3
|
module RFC3464
|
4
4
|
class << self
|
5
5
|
require 'sisimai/lhost'
|
6
|
+
require 'sisimai/string'
|
6
7
|
require 'sisimai/address'
|
8
|
+
require 'sisimai/rfc1123'
|
7
9
|
require 'sisimai/rfc1894'
|
10
|
+
require 'sisimai/rfc2045'
|
11
|
+
require 'sisimai/rfc5322'
|
12
|
+
require 'sisimai/rfc3464/thirdparty'
|
8
13
|
|
9
|
-
# http://tools.ietf.org/html/rfc3464
|
10
14
|
Indicators = Sisimai::Lhost.INDICATORS
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
'your message could not be delivered',
|
19
|
-
'your message was not delivered to ',
|
20
|
-
'your message was not delivered to the following recipients',
|
21
|
-
],
|
22
|
-
rfc822: [
|
23
|
-
'content-type: message/rfc822',
|
24
|
-
'content-type: text/rfc822-headers',
|
25
|
-
'return-path: <'
|
26
|
-
],
|
27
|
-
}.freeze
|
28
|
-
ReadUntil0 = [
|
29
|
-
# Stop reading when the following string have appeared at the first of a line
|
30
|
-
'a copy of the original message below this line:',
|
31
|
-
'content-type: message/delivery-status',
|
32
|
-
'for further assistance, please contact ',
|
33
|
-
'here is a copy of the first part of the message',
|
34
|
-
'received:',
|
35
|
-
'received-from-mta:',
|
36
|
-
'reporting-mta:',
|
37
|
-
'reporting-ua:',
|
38
|
-
'return-path:',
|
39
|
-
'the non-delivered message is attached to this message',
|
40
|
-
].freeze
|
41
|
-
ReadUntil1 = [
|
42
|
-
# Stop reading when the following string have appeared in a line
|
43
|
-
'attachment is a copy of the message',
|
44
|
-
'below is a copy of the original message:',
|
45
|
-
'below this line is a copy of the message',
|
46
|
-
'message contains ',
|
47
|
-
'message text follows: ',
|
48
|
-
'original message follows',
|
49
|
-
'the attachment contains the original mail headers',
|
50
|
-
'the first ',
|
51
|
-
'unsent message below',
|
52
|
-
'your message reads (in part):',
|
53
|
-
].freeze
|
54
|
-
ReadAfter0 = [
|
55
|
-
# Do not read before the following strings
|
56
|
-
' the postfix ',
|
57
|
-
'a summary of the undelivered message you sent follows:',
|
58
|
-
'the following is the error message',
|
59
|
-
'the message that you sent was undeliverable to the following',
|
60
|
-
'your message was not delivered to ',
|
61
|
-
].freeze
|
62
|
-
DoNotRead0 = [' -----', ' -----', '--', '|--', '*'].freeze
|
63
|
-
DoNotRead1 = ['mail from:', 'message-id:', ' from: '].freeze
|
64
|
-
ReadEmail0 = [' ', '"', '<',].freeze
|
65
|
-
ReadEmail1 = [
|
66
|
-
# There is an email address around the following strings
|
67
|
-
'address:',
|
68
|
-
'addressed to',
|
69
|
-
'could not be delivered to:',
|
70
|
-
'delivered to',
|
71
|
-
'delivery failed:',
|
72
|
-
'did not reach the following recipient:',
|
73
|
-
'error-for:',
|
74
|
-
'failed recipient:',
|
75
|
-
'failed to deliver to',
|
76
|
-
'intended recipient:',
|
77
|
-
'mailbox is full:',
|
78
|
-
'recipient:',
|
79
|
-
'rcpt to:',
|
80
|
-
'smtp server <',
|
81
|
-
'the following recipients returned permanent errors:',
|
82
|
-
'the following addresses had permanent errors',
|
83
|
-
'the following message to',
|
84
|
-
'to: ',
|
85
|
-
'unknown user:',
|
86
|
-
'unable to deliver mail to the following recipient',
|
87
|
-
'undeliverable to',
|
88
|
-
'undeliverable address:',
|
89
|
-
'you sent mail to',
|
90
|
-
'your message has encountered delivery problems to the following recipients:',
|
91
|
-
'was automatically rejected',
|
92
|
-
'was rejected due to',
|
15
|
+
Boundaries = [
|
16
|
+
# When the new value added, the part of the value should be listed in delimiters variable
|
17
|
+
# defined at Sisimai::RFC2045.makeFlat() method
|
18
|
+
"Content-Type: message/rfc822",
|
19
|
+
"Content-Type: text/rfc822-headers",
|
20
|
+
"Content-Type: message/partial",
|
21
|
+
"Content-Disposition: inline", # See lhost-amavis-*.eml, lhost-facebook-*.eml
|
93
22
|
].freeze
|
23
|
+
StartingOf = { message: ["Content-Type: message/delivery-status"] }.freeze
|
24
|
+
FieldTable = Sisimai::RFC1894.FIELDTABLE
|
94
25
|
|
95
|
-
#
|
26
|
+
# Decode a bounce mail which have fields defined in RFC3464
|
96
27
|
# @param [Hash] mhead Message headers of a bounce email
|
97
28
|
# @param [String] mbody Message body of a bounce email
|
98
29
|
# @return [Hash] Bounce data list and message/rfc822 part
|
99
30
|
# @return [Nil] it failed to decode or the arguments are missing
|
100
31
|
def inquire(mhead, mbody)
|
101
|
-
|
102
|
-
|
32
|
+
# There is no "Content-Type: message/rfc822" line in the message body
|
33
|
+
if Boundaries.any? { |a| mbody.include?(a) } == false
|
34
|
+
# Insert "Content-Type: message/rfc822" before "Return-Path:" of the original message
|
35
|
+
p0 = mbody.index("\n\nReturn-Path:")
|
36
|
+
mbody = sprintf("%s%s%s", mbody[0, p0], Boundaries[0], mbody[p0 + 1, mbody.size]) if p0
|
37
|
+
end
|
103
38
|
|
39
|
+
permessage = {}
|
104
40
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
itisbounce = false
|
114
|
-
connheader = {
|
115
|
-
'date' => nil, # The value of Arrival-Date header
|
116
|
-
'rhost' => nil, # The value of Reporting-MTA header
|
117
|
-
'lhost' => nil, # The value of Received-From-MTA header
|
118
|
-
}
|
41
|
+
alternates = Sisimai::Lhost.DELIVERYSTATUS
|
42
|
+
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
43
|
+
readslices = [""]
|
44
|
+
readcursor = 0 # (Integer) Points the current cursor position
|
45
|
+
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
46
|
+
beforemesg = "" # (String) String before StartingOf[:message]
|
47
|
+
goestonext = false # (Bool) Flag: do not append the line into beforemesg
|
48
|
+
isboundary = [Sisimai::RFC2045.boundary(mhead["content-type"], 0)]; isboundary[0] ||= ""
|
119
49
|
v = nil
|
120
50
|
|
51
|
+
while emailparts[0].index('@').nil? do
|
52
|
+
# There is no email address in the first element of emailparts
|
53
|
+
# There is a bounce message inside of message/rfc822 part at lhost-x5-*
|
54
|
+
p0 = -1 # The index of the boundary string found first
|
55
|
+
p1 = 0 # Offset position of the message body after the boundary string
|
56
|
+
ct = "" # Boundary string found first such as "Content-Type: message/rfc822"
|
57
|
+
|
58
|
+
Boundaries.each do |e|
|
59
|
+
# Look for a boundary string from the message body
|
60
|
+
p0 = mbody.index(e + "\n"); next if p0.nil?
|
61
|
+
p1 = p0 + e.size + 2
|
62
|
+
ct = e; break
|
63
|
+
end
|
64
|
+
break if p0.nil?
|
65
|
+
|
66
|
+
cx = mbody[p1, mbody.size]
|
67
|
+
p2 = cx.index("\n\n")
|
68
|
+
cv = cx[p2 + 2, mbody.size]
|
69
|
+
emailparts = Sisimai::RFC5322.part(cv, [ct], 0)
|
70
|
+
break
|
71
|
+
end
|
72
|
+
|
73
|
+
if emailparts[0].index(StartingOf[:message][0]) == nil
|
74
|
+
# There is no "Content-Type: message/delivery-status" line in the message body
|
75
|
+
# Insert "Content-Type: message/delivery-status" before "Reporting-MTA:" field
|
76
|
+
cv = "\nReporting-MTA:"
|
77
|
+
e0 = emailparts[0]
|
78
|
+
p0 = e0.index(cv)
|
79
|
+
emailparts[0] = sprintf("%s\n\n%s%s", e0[0, p0], StartingOf[:message][0], e0[p0, e0.size]) if p0
|
80
|
+
end
|
81
|
+
|
82
|
+
%w[Final-Recipient Original-Recipient].each do |e|
|
83
|
+
# Fix the malformed field "Final-Recipient: <kijitora@example.jp>"
|
84
|
+
cv = "\n" + e + ": "
|
85
|
+
cx = cv + "<"
|
86
|
+
p0 = emailparts[0].index(cx); next if p0.nil?
|
87
|
+
|
88
|
+
emailparts[0] = emailparts[0].sub(": <", ": rfc822; ")
|
89
|
+
p1 = emailparts[0].index(">\n", p0 + 2); emailparts[0][p1, 1] = ""
|
90
|
+
end
|
91
|
+
|
92
|
+
bodyslices = emailparts[0].scrub('?').split("\n")
|
121
93
|
while e = bodyslices.shift do
|
122
94
|
# Read error messages and delivery status lines from the head of the email to the previous
|
123
95
|
# line of the beginning of the original message.
|
124
96
|
readslices << e # Save the current line for the next loop
|
125
|
-
lowercased = e.downcase
|
126
97
|
|
127
98
|
if readcursor == 0
|
128
99
|
# Beginning of the bounce message or delivery status part
|
129
|
-
|
130
|
-
readcursor |= Indicators[:deliverystatus]
|
131
|
-
next
|
132
|
-
end
|
133
|
-
end
|
100
|
+
readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
|
134
101
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
102
|
+
while true do
|
103
|
+
# Append each string before startingof["message"][0] except the following patterns
|
104
|
+
# for the later reference
|
105
|
+
break if e.empty? # Blank line
|
106
|
+
break if goestonext # Skip if the part is text/html, image/icon, in multipart/*
|
107
|
+
|
108
|
+
# This line is a boundary kept in "multiparts" as a string, when the end of the boundary
|
109
|
+
# appeared, the condition above also returns true.
|
110
|
+
if isboundary.any? { |a| e == a } then goestonext = false; break; end
|
111
|
+
if e.start_with?("Content-Type:")
|
112
|
+
# Content-Type: field in multipart/*
|
113
|
+
if e.include?("multipart/")
|
114
|
+
# Content-Type: multipart/alternative; boundary=aa00220022222222ffeebb
|
115
|
+
# Pick the boundary string and store it into "isboucdary"
|
116
|
+
isboundary << Sisimai::RFC2045.boundary(e, 0)
|
117
|
+
elsif e.include?("text/plain")
|
118
|
+
# Content-Type: "text/plain"
|
119
|
+
goestonext = false
|
120
|
+
else
|
121
|
+
# Other types: for example, text/html, image/jpg, and so on
|
122
|
+
goestonext = true
|
123
|
+
end
|
124
|
+
break
|
125
|
+
end
|
142
126
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
break if
|
148
|
-
|
127
|
+
break if e.start_with?("Content-") # Content-Disposition, ...
|
128
|
+
break if e.start_with?("This is a MIME") # This is a MIME-formatted message.
|
129
|
+
break if e.start_with?("This is a multi") # This is a multipart message in MIME format
|
130
|
+
break if e.start_with?("This is an auto") # This is an automatically generated ...
|
131
|
+
break if e.start_with?("This multi-part") # This multi-part MIME message contains...
|
132
|
+
break if e.start_with?("###") # A frame like #####
|
133
|
+
break if e.start_with?("***") # A frame like *****
|
134
|
+
break if e.start_with?("--") # Boundary string
|
135
|
+
break if e.include?("--- The follow") # ----- The following addresses had delivery problems -----
|
136
|
+
break if e.include?("--- Transcript") # ----- Transcript of session follows -----
|
137
|
+
beforemesg << e + " "; break
|
149
138
|
end
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
next if e.empty?
|
139
|
+
next
|
140
|
+
end
|
141
|
+
next if (readcursor & Indicators[:deliverystatus]) == 0
|
142
|
+
next if e.empty?
|
155
143
|
|
144
|
+
f = Sisimai::RFC1894.match(e)
|
145
|
+
if f > 0
|
146
|
+
# "e" matched with any field defined in RFC3464
|
147
|
+
next unless o = Sisimai::RFC1894.field(e)
|
156
148
|
v = dscontents[-1]
|
157
|
-
if f = Sisimai::RFC1894.match(e)
|
158
|
-
# "e" matched with any field defined in RFC3464
|
159
|
-
next unless o = Sisimai::RFC1894.field(e)
|
160
149
|
|
161
|
-
|
150
|
+
if o[3] == "addr"
|
151
|
+
# Final-Recipient: rfc822; kijitora@example.jp
|
152
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
153
|
+
if o[0] == "final-recipient"
|
162
154
|
# Final-Recipient: rfc822; kijitora@example.jp
|
163
|
-
#
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
x = v['recipient'] || ''
|
172
|
-
y = Sisimai::Address.s3s4(o[2])
|
173
|
-
y = maybealias unless Sisimai::Address.is_emailaddress(y)
|
174
|
-
|
175
|
-
if !x.empty? && x != y
|
176
|
-
# There are multiple recipient addresses in the message body.
|
177
|
-
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
178
|
-
v = dscontents[-1]
|
179
|
-
end
|
180
|
-
v['recipient'] = y
|
181
|
-
recipients += 1
|
182
|
-
itisbounce ||= true
|
183
|
-
|
184
|
-
v['alias'] ||= maybealias
|
185
|
-
maybealias = nil
|
186
|
-
end
|
187
|
-
elsif o[0] == 'x-actual-recipient'
|
188
|
-
# X-Actual-Recipient: RFC822; |IFS=' ' && exec procmail -f- || exit 75 ...
|
189
|
-
# X-Actual-Recipient: rfc822; kijitora@neko.example.jp
|
190
|
-
v['alias'] = o[2] unless o[2].include?(' ')
|
155
|
+
# Final-Recipient: x400; /PN=...
|
156
|
+
cv = Sisimai::Address.s3s4(o[2]); next unless Sisimai::Address.is_emailaddress(cv)
|
157
|
+
cw = dscontents.size; next if cw > 0 && cv == dscontents[cw - 1]["recipient"]
|
158
|
+
|
159
|
+
if v["recipient"] != ""
|
160
|
+
# There are multiple recipient addresses in the message body.
|
161
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
162
|
+
v = dscontents[-1]
|
191
163
|
end
|
192
|
-
|
193
|
-
|
194
|
-
v['spec'] = o[1]
|
195
|
-
v['diagnosis'] = o[2]
|
164
|
+
v["recipient"] = cv
|
165
|
+
recipients += 1
|
196
166
|
else
|
197
|
-
#
|
198
|
-
|
199
|
-
v[fieldtable[o[0]]] = o[2]
|
200
|
-
|
201
|
-
next unless f
|
202
|
-
permessage[fieldtable[o[0]]] = o[2]
|
167
|
+
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
168
|
+
v["alias"] = o[2]
|
203
169
|
end
|
170
|
+
elsif o[3] == "code"
|
171
|
+
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
172
|
+
v["spec"] = o[1]
|
173
|
+
v["diagnosis"] = o[2]
|
204
174
|
else
|
205
|
-
#
|
206
|
-
if
|
207
|
-
# There
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
# Status: 553 Exceeded maximum inbound message size
|
212
|
-
v['alterrors'] = e[8, e.size]
|
213
|
-
|
214
|
-
elsif readslices[-2].start_with?('Diagnostic-Code:') && cv = e.start_with?(' ')
|
215
|
-
# Continued line of the value of Diagnostic-Code header
|
216
|
-
v['diagnosis'] << ' ' << e
|
217
|
-
readslices[-1] = 'Diagnostic-Code: ' << e
|
218
|
-
else
|
219
|
-
# Get error messages which is written in the message body directly
|
220
|
-
next if e.start_with?(' ', ' ', 'X')
|
221
|
-
cr = Sisimai::SMTP::Reply.find(e) || ''
|
222
|
-
ca = Sisimai::Address.find(e) || []
|
223
|
-
co = Sisimai::String.aligned(e, ['<', '@', '>'])
|
224
|
-
|
225
|
-
if cr.size > 0 || (ca.size > 0 && co)
|
226
|
-
v['alterrors'] ||= ' '
|
227
|
-
v['alterrors'] << ' ' << e
|
228
|
-
end
|
175
|
+
# Other DSN fields defined in RFC3464
|
176
|
+
if o[4].size > 0
|
177
|
+
# There are other error messages as a comment such as the following:
|
178
|
+
# Status: 5.0.0 (permanent failure)
|
179
|
+
# Status: 4.0.0 (cat.example.net: host name lookup failure)
|
180
|
+
v["diagnosis"] << " " + o[4] + " "
|
229
181
|
end
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
# -----------------------------------------------------------------------------------------
|
235
|
-
while true
|
236
|
-
# Fallback, decode the entire message body
|
237
|
-
break if recipients > 0
|
182
|
+
next unless FieldTable[o[0]]
|
183
|
+
next if o[3] == "host" && Sisimai::RFC1123.is_internethost(o[2]) == false
|
184
|
+
v[FieldTable[o[0]]] = o[2]
|
238
185
|
|
239
|
-
|
240
|
-
|
241
|
-
headerfrom = (mhead['from'] || '').downcase
|
242
|
-
errortitle = (mhead['subject'] || '').downcase
|
243
|
-
patternsof = {
|
244
|
-
'from' => ['postmaster@', 'mailer-daemon@', 'root@'],
|
245
|
-
'return-path' => ['<>', 'mailer-daemon'],
|
246
|
-
'subject' => ['delivery fail', 'delivery report', 'failure notice', 'mail delivery',
|
247
|
-
'mail failed', 'mail error', 'non-delivery', 'returned mail',
|
248
|
-
'undeliverable mail', 'warning: '],
|
249
|
-
}
|
250
|
-
|
251
|
-
match = nil
|
252
|
-
match ||= patternsof['from'].any? { |v| headerfrom.include?(v) }
|
253
|
-
match ||= patternsof['subject'].any? { |v| errortitle.include?(v) }
|
254
|
-
match ||= patternsof['return-path'].any? { |v| returnpath.include?(v) }
|
255
|
-
break unless match
|
256
|
-
|
257
|
-
b = dscontents[-1]
|
258
|
-
hasmatched = 0 # There may be an email address around the line
|
259
|
-
readslices = [] # Previous line of this loop
|
260
|
-
|
261
|
-
ReadAfter0.each do |e|
|
262
|
-
# Cut strings from the begining of "mbody" to the strings defined in ReadAfter0
|
263
|
-
i = mbody.downcase.index(e)
|
264
|
-
next unless i
|
265
|
-
mbody = mbody[i, mbody.size - i]
|
266
|
-
end
|
267
|
-
lowercased = mbody.downcase
|
268
|
-
bodyslices = mbody.split("\n")
|
269
|
-
|
270
|
-
while e = bodyslices.shift do
|
271
|
-
# Get the recipient's email address and error messages.
|
272
|
-
next if e.empty?
|
273
|
-
hasmatched = 0
|
274
|
-
lowercased = e.downcase
|
275
|
-
readslices << lowercased
|
276
|
-
|
277
|
-
break if StartingOf[:rfc822].include?(lowercased)
|
278
|
-
break if ReadUntil0.any? { |v| lowercased.start_with?(v) }
|
279
|
-
break if ReadUntil1.any? { |v| lowercased.include?(v) }
|
280
|
-
next if DoNotRead0.any? { |v| lowercased.start_with?(v) }
|
281
|
-
next if DoNotRead1.any? { |v| lowercased.include?(v) }
|
282
|
-
|
283
|
-
while true do
|
284
|
-
# There is an email address with an error message at this line(1)
|
285
|
-
break unless ReadEmail0.any? { |v| lowercased.start_with?(v) }
|
286
|
-
break unless lowercased.include?('@')
|
287
|
-
|
288
|
-
hasmatched = 1
|
289
|
-
break
|
186
|
+
next unless f == 1
|
187
|
+
permessage[FieldTable[o[0]]] = o[2]
|
290
188
|
end
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
break unless ReadEmail1.any? { |v| readslices[-2].include?(v) }
|
307
|
-
break unless lowercased.include?('@') # Must contain '@'
|
308
|
-
break unless lowercased.include?('.') # Must contain '.'
|
309
|
-
break if lowercased.include?('$')
|
310
|
-
|
311
|
-
hasmatched = 3
|
312
|
-
break
|
313
|
-
end
|
314
|
-
|
315
|
-
if hasmatched > 0 && lowercased.include?('@')
|
316
|
-
# May be an email address
|
317
|
-
w = e.split(' ')
|
318
|
-
x = b['recipient'] || ''
|
319
|
-
y = ''
|
320
|
-
|
321
|
-
w.each do |ee|
|
322
|
-
# Find an email address (including "@")
|
323
|
-
next unless ee.include?('@')
|
324
|
-
y = Sisimai::Address.s3s4(ee)
|
325
|
-
next unless Sisimai::Address.is_emailaddress(y)
|
326
|
-
break
|
189
|
+
else
|
190
|
+
# Check that the line is a continued line of the value of Diagnostic-Code: field or not
|
191
|
+
if e.start_with?("X-") && e.include?(": ")
|
192
|
+
# This line is a MTA-Specific fields begins with "X-"
|
193
|
+
next unless Sisimai::RFC3464::ThirdParty.is3rdparty(e)
|
194
|
+
cv = Sisimai::RFC3464::ThirdParty.xfield(e)
|
195
|
+
|
196
|
+
if cv.size > 0 && FieldTable[cv[0].downcase] == nil
|
197
|
+
# Check the first element is a field defined in RFC1894 or not
|
198
|
+
p1 = cv[4].index(":")
|
199
|
+
v["reason"] = cv[4][p1 + 1, cv[4].size] if cv[4].start_with?("reason:")
|
200
|
+
else
|
201
|
+
# Set the value picked from "X-*" field to $dscontents when the current value is empty
|
202
|
+
z = FieldTable[cv[0].downcase]; next unless z
|
203
|
+
v[z] ||= cv[2]
|
327
204
|
end
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
205
|
+
else
|
206
|
+
# The line may be a continued line of the value of the Diagnostic-Code: field
|
207
|
+
if readslices[-2].start_with?("Diagnostic-Code:") == false
|
208
|
+
# In the case of multiple "message/delivery-status" line
|
209
|
+
next if e.start_with?("Content-") # Content-Disposition:, ...
|
210
|
+
next if e.start_with?("--") # Boundary string
|
211
|
+
beforemesg << e + " "; next
|
333
212
|
end
|
334
|
-
b['recipient'] = y
|
335
|
-
recipients += 1
|
336
|
-
itisbounce ||= true
|
337
213
|
|
338
|
-
|
339
|
-
#
|
340
|
-
|
214
|
+
# Diagnostic-Code: SMTP; 550-5.7.26 The MAIL FROM domain [email.example.jp]
|
215
|
+
# has an SPF record with a hard fail
|
216
|
+
next unless e.start_with?(" ")
|
217
|
+
v["diagnosis"] << " " + Sisimai::String.sweep(e)
|
341
218
|
end
|
342
|
-
b['diagnosis'] ||= ''
|
343
|
-
b['diagnosis'] << ' ' << e
|
344
219
|
end
|
345
|
-
|
346
|
-
break
|
347
220
|
end
|
348
|
-
return nil unless itisbounce
|
349
221
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
222
|
+
while recipients == 0 do
|
223
|
+
# There is no valid recipient address, Try to use the alias addaress as a final recipient
|
224
|
+
break if dscontents[0]["alias"].nil? || dscontents[0]["alias"].empty?
|
225
|
+
break if Sisimai::Address.is_emailaddress(dscontents[0]["alias"]) == false
|
226
|
+
dscontents[0]["recipient"] = dscontents[0]["alias"]
|
227
|
+
recipients += 1
|
228
|
+
end
|
229
|
+
return nil if recipients == 0
|
230
|
+
|
231
|
+
require "sisimai/smtp/reply"
|
232
|
+
require "sisimai/smtp/status"
|
233
|
+
require "sisimai/smtp/command"
|
234
|
+
|
235
|
+
if beforemesg != ""
|
236
|
+
# Pick some values of $dscontents from the string before StartingOf[:message]
|
237
|
+
beforemesg = Sisimai::String.sweep(beforemesg)
|
238
|
+
alternates["command"] = Sisimai::SMTP::Command.find(beforemesg)
|
239
|
+
alternates["replycode"] = Sisimai::SMTP::Reply.find(beforemesg, dscontents[0]["status"])
|
240
|
+
alternates["status"] = Sisimai::SMTP::Status.find(beforemesg, alternates["replycode"])
|
361
241
|
end
|
362
|
-
|
242
|
+
issuedcode = beforemesg.downcase
|
363
243
|
|
364
|
-
require 'sisimai/smtp/command'
|
365
|
-
require 'sisimai/mda'
|
366
|
-
mdabounced = Sisimai::MDA.inquire(mhead, mbody)
|
367
244
|
dscontents.each do |e|
|
368
|
-
# Set default values if each value is empty.
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
245
|
+
# Set default values stored in "permessage" if each value in "dscontents" is empty.
|
246
|
+
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
247
|
+
e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"])
|
248
|
+
lowercased = e["diagnosis"].downcase
|
249
|
+
|
250
|
+
if recipients == 1
|
251
|
+
# Do not mix the error message of each recipient with "beforemesg" when there is
|
252
|
+
# multiple recipient addresses in the bounce message
|
253
|
+
if issuedcode.include?(lowercased)
|
254
|
+
# beforemesg contains the entire strings of e["diagnosis"]
|
255
|
+
e["diagnosis"] = beforemesg
|
256
|
+
else
|
257
|
+
# The value of e["diagnosis"] is not contained in $beforemesg
|
258
|
+
# There may be an important error message in $beforemesg
|
259
|
+
e["diagnosis"] = Sisimai::String.sweep(sprintf("%s %s", beforemesg, e["diagnosis"]))
|
380
260
|
end
|
381
261
|
end
|
382
|
-
e[
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
e['diagnosis'] = mdabounced['message'] unless mdabounced['message'].empty?
|
389
|
-
e['command'] = nil
|
390
|
-
end
|
391
|
-
|
392
|
-
e['date'] ||= mhead['date']
|
393
|
-
e['status'] ||= Sisimai::SMTP::Status.find(e['diagnosis']) || ''
|
394
|
-
e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis'])
|
262
|
+
e["command"] = Sisimai::SMTP::Command.find(e["diagnosis"])
|
263
|
+
e["command"] = alternates["command"] if e["command"].empty?
|
264
|
+
e["replycode"] = Sisimai::SMTP::Reply.find(e["diagnosis"], e["status"])
|
265
|
+
e["replycode"] = alternates["replycode"] if e["replycode"].empty?
|
266
|
+
e["status"] = Sisimai::SMTP::Status.find(e["diagnosis"], e["replycode"]) if e["status"].empty?
|
267
|
+
e["status"] = alternates["status"] if e["status"].empty?
|
395
268
|
end
|
396
269
|
|
397
|
-
|
270
|
+
if emailparts[1].nil? || emailparts[1].empty?
|
271
|
+
# Set the recipient address as To: header in the original message part
|
272
|
+
emailparts[1] = sprintf("To: <%s>\n", dscontents[0]["recipient"])
|
273
|
+
end
|
274
|
+
return { "ds" => dscontents, "rfc822" => emailparts[1] }
|
398
275
|
end
|
399
|
-
|
276
|
+
|
277
|
+
def description; 'RFC3464'; end
|
400
278
|
end
|
401
279
|
end
|
402
280
|
end
|
281
|
+
|