sisimai 5.4.0 → 5.5.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/codecovio.yml +1 -1
- data/.github/workflows/rake-test.yml +1 -1
- data/ChangeLog.md +45 -0
- data/Makefile +2 -4
- data/README-JA.md +23 -20
- data/README.md +23 -20
- data/lib/sisimai/address.rb +92 -44
- data/lib/sisimai/arf.rb +7 -8
- data/lib/sisimai/datetime.rb +2 -2
- data/lib/sisimai/fact/json.rb +1 -2
- data/lib/sisimai/fact/yaml.rb +1 -2
- data/lib/sisimai/fact.rb +60 -35
- data/lib/sisimai/lda.rb +2 -2
- data/lib/sisimai/lhost/activehunter.rb +4 -5
- data/lib/sisimai/lhost/amazonses.rb +3 -4
- data/lib/sisimai/lhost/apachejames.rb +2 -2
- data/lib/sisimai/lhost/biglobe.rb +6 -6
- data/lib/sisimai/lhost/courier.rb +7 -7
- data/lib/sisimai/lhost/domino.rb +6 -6
- data/lib/sisimai/lhost/dragonfly.rb +5 -5
- data/lib/sisimai/lhost/einsundeins.rb +4 -4
- data/lib/sisimai/lhost/exchange2003.rb +7 -7
- data/lib/sisimai/lhost/exchange2007.rb +3 -3
- data/lib/sisimai/lhost/exim.rb +7 -7
- data/lib/sisimai/lhost/ezweb.rb +3 -2
- data/lib/sisimai/lhost/fml.rb +9 -9
- data/lib/sisimai/lhost/gmail.rb +9 -9
- data/lib/sisimai/lhost/gmx.rb +3 -3
- data/lib/sisimai/lhost/googlegroups.rb +6 -7
- data/lib/sisimai/lhost/googleworkspace.rb +5 -6
- data/lib/sisimai/lhost/imailserver.rb +4 -4
- data/lib/sisimai/lhost/kddi.rb +4 -4
- data/lib/sisimai/lhost/mailfoundry.rb +3 -3
- data/lib/sisimai/lhost/{mailmarshalsmtp.rb → mailmarshal.rb} +5 -5
- data/lib/sisimai/lhost/messagingserver.rb +8 -8
- data/lib/sisimai/lhost/mfilter.rb +8 -4
- data/lib/sisimai/lhost/mimecast.rb +105 -0
- data/lib/sisimai/lhost/notes.rb +5 -5
- data/lib/sisimai/lhost/opensmtpd.rb +5 -5
- data/lib/sisimai/lhost/postfix.rb +38 -32
- data/lib/sisimai/lhost/qmail.rb +6 -6
- data/lib/sisimai/lhost/sendmail.rb +13 -13
- data/lib/sisimai/lhost/{interscanmss.rb → trendmicro.rb} +8 -9
- data/lib/sisimai/lhost/v5sendmail.rb +7 -7
- data/lib/sisimai/lhost/verizon.rb +3 -3
- data/lib/sisimai/lhost/x1.rb +7 -4
- data/lib/sisimai/lhost/x2.rb +40 -12
- data/lib/sisimai/lhost/x3.rb +3 -3
- data/lib/sisimai/lhost/x6.rb +2 -2
- data/lib/sisimai/lhost/zoho.rb +5 -5
- data/lib/sisimai/lhost.rb +18 -17
- data/lib/sisimai/mail/maildir.rb +4 -4
- data/lib/sisimai/mail/mbox.rb +4 -4
- data/lib/sisimai/mail/memory.rb +1 -1
- data/lib/sisimai/mail/stdin.rb +2 -2
- data/lib/sisimai/message.rb +34 -34
- data/lib/sisimai/order.rb +5 -4
- data/lib/sisimai/reason/authfailure.rb +1 -1
- data/lib/sisimai/reason/badreputation.rb +1 -1
- data/lib/sisimai/reason/blocked.rb +2 -1
- data/lib/sisimai/reason/contenterror.rb +1 -2
- data/lib/sisimai/reason/exceedlimit.rb +1 -1
- data/lib/sisimai/reason/expired.rb +1 -1
- data/lib/sisimai/reason/failedstarttls.rb +1 -1
- data/lib/sisimai/reason/filtered.rb +1 -1
- data/lib/sisimai/reason/hasmoved.rb +1 -1
- data/lib/sisimai/reason/hostunknown.rb +2 -2
- data/lib/sisimai/reason/mailboxfull.rb +1 -1
- data/lib/sisimai/reason/mailererror.rb +1 -1
- data/lib/sisimai/reason/mesgtoobig.rb +1 -1
- data/lib/sisimai/reason/networkerror.rb +1 -1
- data/lib/sisimai/reason/norelaying.rb +2 -2
- data/lib/sisimai/reason/notaccept.rb +2 -3
- data/lib/sisimai/reason/notcompliantrfc.rb +1 -1
- data/lib/sisimai/reason/policyviolation.rb +2 -4
- data/lib/sisimai/reason/rejected.rb +5 -3
- data/lib/sisimai/reason/requireptr.rb +1 -1
- data/lib/sisimai/reason/securityerror.rb +1 -1
- data/lib/sisimai/reason/spamdetected.rb +1 -1
- data/lib/sisimai/reason/speeding.rb +1 -1
- data/lib/sisimai/reason/suspend.rb +2 -1
- data/lib/sisimai/reason/systemerror.rb +1 -1
- data/lib/sisimai/reason/systemfull.rb +1 -1
- data/lib/sisimai/reason/toomanyconn.rb +1 -1
- data/lib/sisimai/reason/userunknown.rb +4 -3
- data/lib/sisimai/reason/vacation.rb +1 -1
- data/lib/sisimai/reason/virusdetected.rb +1 -1
- data/lib/sisimai/reason.rb +12 -12
- data/lib/sisimai/rfc1123.rb +58 -18
- data/lib/sisimai/rfc1894.rb +6 -8
- data/lib/sisimai/rfc2045.rb +25 -13
- data/lib/sisimai/rfc3464/thirdparty.rb +2 -3
- data/lib/sisimai/rfc3464.rb +6 -6
- data/lib/sisimai/rfc3834.rb +18 -8
- data/lib/sisimai/rfc5322.rb +9 -9
- data/lib/sisimai/rfc791.rb +2 -2
- data/lib/sisimai/rhost/aol.rb +4 -1
- data/lib/sisimai/rhost/apple.rb +11 -7
- data/lib/sisimai/rhost/cox.rb +9 -5
- data/lib/sisimai/rhost/facebook.rb +9 -3
- data/lib/sisimai/rhost/franceptt.rb +86 -37
- data/lib/sisimai/rhost/godaddy.rb +10 -1
- data/lib/sisimai/rhost/google.rb +26 -22
- data/lib/sisimai/rhost/gsuite.rb +1 -1
- data/lib/sisimai/rhost/kddi.rb +1 -1
- data/lib/sisimai/rhost/messagelabs.rb +160 -2
- data/lib/sisimai/rhost/microsoft.rb +80 -29
- data/lib/sisimai/rhost/mimecast.rb +30 -21
- data/lib/sisimai/rhost/nttdocomo.rb +69 -89
- data/lib/sisimai/rhost/outlook.rb +1 -1
- data/lib/sisimai/rhost/spectrum.rb +1 -1
- data/lib/sisimai/rhost/tencent.rb +5 -4
- data/lib/sisimai/rhost/yahooinc.rb +2 -2
- data/lib/sisimai/rhost/zoho.rb +72 -0
- data/lib/sisimai/rhost.rb +5 -4
- data/lib/sisimai/smtp/command.rb +2 -2
- data/lib/sisimai/smtp/reply.rb +11 -4
- data/lib/sisimai/smtp/status.rb +17 -8
- data/lib/sisimai/smtp/transcript.rb +3 -3
- data/lib/sisimai/string.rb +6 -10
- data/lib/sisimai/version.rb +1 -1
- data/lib/sisimai.rb +1 -1
- data/set-of-emails/maildir/bsd/lhost-exim-56.eml +40 -40
- data/set-of-emails/maildir/bsd/lhost-mfilter-05.eml +41 -0
- data/set-of-emails/maildir/bsd/lhost-mimecast-01.eml +46 -0
- data/set-of-emails/maildir/bsd/lhost-mimecast-02.eml +59 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-79.eml +81 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-80.eml +84 -0
- data/set-of-emails/maildir/bsd/lhost-x1-03.eml +26 -0
- data/set-of-emails/maildir/bsd/lhost-x1-04.eml +45 -0
- data/set-of-emails/maildir/bsd/lhost-x2-07.eml +30 -0
- data/set-of-emails/maildir/bsd/rfc3464-66.eml +170 -0
- data/set-of-emails/maildir/bsd/rfc3834-06.eml +59 -0
- data/set-of-emails/maildir/bsd/rhost-apple-05.eml +96 -0
- data/set-of-emails/maildir/bsd/rhost-zoho-01.eml +88 -0
- data/set-of-emails/maildir/bsd/rhost-zoho-02.eml +86 -0
- data/set-of-emails/maildir/bsd/rhost-zoho-03.eml +87 -0
- data/set-of-emails/maildir/bsd/rhost-zoho-04.eml +86 -0
- data/set-of-emails/maildir/not/rb-issue-368-bug.eml +39 -0
- data/sisimai-java.gemspec +1 -1
- data/sisimai.gemspec +1 -1
- metadata +27 -9
- /data/set-of-emails/maildir/bsd/{lhost-mailmarshalsmtp-02.eml → lhost-mailmarshal-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-interscanmss-01.eml → lhost-trendmicro-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-interscanmss-02.eml → lhost-trendmicro-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-interscanmss-03.eml → lhost-trendmicro-03.eml} +0 -0
data/lib/sisimai/rfc2045.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Sisimai
|
|
|
9
9
|
# @param [String] argvs String to be checked
|
|
10
10
|
# @return [Boolean] false: Not MIME encoded string, true: MIME encoded string
|
|
11
11
|
def is_encoded(argv1)
|
|
12
|
-
return false
|
|
12
|
+
return false if argv1.nil? || argv1.empty?
|
|
13
13
|
|
|
14
14
|
text1 = argv1.delete('"')
|
|
15
15
|
mime1 = false
|
|
@@ -69,7 +69,7 @@ module Sisimai
|
|
|
69
69
|
# utf8 => UTF-8
|
|
70
70
|
ctxcharset = 'UTF-8' if ctxcharset.casecmp('UTF8') == 0
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
if ctxcharset.casecmp('UTF-8') != 0
|
|
73
73
|
# Characterset is not UTF-8
|
|
74
74
|
begin
|
|
75
75
|
p = p.encode!('UTF-8', ctxcharset)
|
|
@@ -109,12 +109,11 @@ module Sisimai
|
|
|
109
109
|
def parameter(argv0 = '', argv1 = '')
|
|
110
110
|
return "" if argv0.empty?
|
|
111
111
|
parameterq = argv1.size > 0 ? argv1 + '=' : ''
|
|
112
|
-
paramindex = argv1.size > 0 ? argv0.index(parameterq) : 0
|
|
113
|
-
return '' unless paramindex
|
|
112
|
+
paramindex = argv1.size > 0 ? argv0.index(parameterq) : 0; return '' if paramindex.nil?
|
|
114
113
|
|
|
115
114
|
# Find the value of the parameter name specified in argv1
|
|
116
115
|
foundtoken = argv0[paramindex + parameterq.size, argv0.size].split(';', 2)[0] || ''
|
|
117
|
-
foundtoken = foundtoken.downcase
|
|
116
|
+
foundtoken = foundtoken.downcase if argv1 != 'boundary'
|
|
118
117
|
foundtoken = foundtoken.delete('"').delete("'")
|
|
119
118
|
return foundtoken
|
|
120
119
|
end
|
|
@@ -147,8 +146,8 @@ module Sisimai
|
|
|
147
146
|
return nil if block.empty?
|
|
148
147
|
|
|
149
148
|
(upperchunk, lowerchunk) = block.split("\n\n", 2)
|
|
150
|
-
return ['', ''] if
|
|
151
|
-
return ['', '']
|
|
149
|
+
return ['', ''] if upperchunk.nil? || upperchunk.empty?
|
|
150
|
+
return ['', ''] if upperchunk.index('Content-Type').nil?
|
|
152
151
|
|
|
153
152
|
headerpart = ['', ''] # ["text/plain; charset=iso-2022-jp; ...", "quoted-printable"]
|
|
154
153
|
multipart1 = [] # [headerpart, "body"]
|
|
@@ -224,8 +223,10 @@ module Sisimai
|
|
|
224
223
|
multiparts = argv1.split(Regexp.new(Regexp.escape(boundary01) + "\n"))
|
|
225
224
|
partstable = []
|
|
226
225
|
|
|
227
|
-
|
|
228
|
-
multiparts.
|
|
226
|
+
# Remove empty or useless preamble and epilogue of multipart/* block
|
|
227
|
+
multiparts.shift if multiparts[0].size < 8
|
|
228
|
+
return [] if multiparts.empty?
|
|
229
|
+
multiparts.pop if multiparts.size > 2 && multiparts[-1].size < 8
|
|
229
230
|
|
|
230
231
|
while e = multiparts.shift do
|
|
231
232
|
# Check each part and breaks up internal multipart/* block
|
|
@@ -234,8 +235,7 @@ module Sisimai
|
|
|
234
235
|
# There is nested multipart/* block
|
|
235
236
|
boundary02 = boundary(f[0], -1); next if boundary02.empty?
|
|
236
237
|
bodyinside = f[-1].split("\n\n", 2)[-1]
|
|
237
|
-
next
|
|
238
|
-
next unless bodyinside.index(boundary02)
|
|
238
|
+
next if bodyinside.size < 9 || bodyinside.index(boundary02).nil?
|
|
239
239
|
|
|
240
240
|
v = levelout(f[0], bodyinside)
|
|
241
241
|
partstable += v if v.size > 0
|
|
@@ -299,6 +299,16 @@ module Sisimai
|
|
|
299
299
|
if ctencoding == 'base64'
|
|
300
300
|
# Content-Transfer-Encoding: base64
|
|
301
301
|
bodystring = decodeB(bodyinside) || ''
|
|
302
|
+
dontappend = false; while first10 = bodystring[0,10] do
|
|
303
|
+
# Don't pick the decoded part as an error message when the part is
|
|
304
|
+
# - BASE64 encoded.
|
|
305
|
+
# - the value of the charset is not utf-8.
|
|
306
|
+
# - NOT a plain text.
|
|
307
|
+
break if Sisimai::String.aligned(e[0], ['charset', '=', 'utf-8'])
|
|
308
|
+
break unless first10 =~ /[\x00-\x08\x0E-\x1F\x7F-]/
|
|
309
|
+
dontappend = true; break
|
|
310
|
+
end
|
|
311
|
+
next if dontappend
|
|
302
312
|
|
|
303
313
|
elsif ctencoding == 'quoted-printable'
|
|
304
314
|
# Content-Transfer-Encoding: quoted-printable
|
|
@@ -328,7 +338,7 @@ module Sisimai
|
|
|
328
338
|
# the following errors:
|
|
329
339
|
# - incompatible character encodings: ASCII-8BIT and UTF-8
|
|
330
340
|
# - invalid byte sequence in UTF-8
|
|
331
|
-
|
|
341
|
+
if bodystring.encoding.to_s != 'UTF-8'
|
|
332
342
|
# ASCII-8BIT or other 8bit encodings
|
|
333
343
|
ctxcharset = parameter(e[0], 'charset')
|
|
334
344
|
if ctxcharset.empty?
|
|
@@ -346,6 +356,8 @@ module Sisimai
|
|
|
346
356
|
|
|
347
357
|
else
|
|
348
358
|
# There is no Content-Transfer-Encoding header in the part
|
|
359
|
+
be = bodyinside.encoding.to_s
|
|
360
|
+
bodyinside = Sisimai::String.to_utf8(bodyinside, be) if be != 'UTF-8'
|
|
349
361
|
bodystring += bodyinside
|
|
350
362
|
end
|
|
351
363
|
|
|
@@ -357,7 +369,7 @@ module Sisimai
|
|
|
357
369
|
end
|
|
358
370
|
|
|
359
371
|
# Append "\n" when the last character of $bodystring is not LF
|
|
360
|
-
bodystring += "\n\n"
|
|
372
|
+
bodystring += "\n\n" if bodystring[-2, 2] != "\n\n"
|
|
361
373
|
flattenout += bodystring
|
|
362
374
|
end
|
|
363
375
|
|
|
@@ -22,8 +22,7 @@ module Sisimai
|
|
|
22
22
|
# @param string argv1 A line of a bounce mail
|
|
23
23
|
# @return string An MTA name of the 3rd party
|
|
24
24
|
def returnedby(argv1 = "")
|
|
25
|
-
return ""
|
|
26
|
-
return "" unless argv1.start_with?("X-")
|
|
25
|
+
return "" if argv1.nil? || argv1.start_with?("X-") == false
|
|
27
26
|
|
|
28
27
|
ThirdParty.each_key do |e|
|
|
29
28
|
# Does the argument include the 3rd party specific field?
|
|
@@ -53,7 +52,7 @@ module Sisimai
|
|
|
53
52
|
MessagesOf = {
|
|
54
53
|
"bad-domain" => "hostunknown",
|
|
55
54
|
"bad-mailbox" => "userunknown",
|
|
56
|
-
"inactive-mailbox" => "
|
|
55
|
+
"inactive-mailbox" => "suspend",
|
|
57
56
|
"message-expired" => "expired",
|
|
58
57
|
"no-answer-from-host" => "networkerror",
|
|
59
58
|
"policy-related" => "policyviolation",
|
data/lib/sisimai/rfc3464.rb
CHANGED
|
@@ -151,7 +151,7 @@ module Sisimai
|
|
|
151
151
|
if o[0] == "final-recipient"
|
|
152
152
|
# Final-Recipient: rfc822; kijitora@example.jp
|
|
153
153
|
# Final-Recipient: x400; /PN=...
|
|
154
|
-
cv = Sisimai::Address.s3s4(o[2]); next
|
|
154
|
+
cv = Sisimai::Address.s3s4(o[2]); next if Sisimai::Address.is_emailaddress(cv) == false
|
|
155
155
|
cw = dscontents.size; next if cw > 0 && cv == dscontents[cw - 1]["recipient"]
|
|
156
156
|
|
|
157
157
|
if v["recipient"] != ""
|
|
@@ -177,18 +177,18 @@ module Sisimai
|
|
|
177
177
|
# Status: 4.0.0 (cat.example.net: host name lookup failure)
|
|
178
178
|
v["diagnosis"] += " #{o[4]} "
|
|
179
179
|
end
|
|
180
|
-
next
|
|
180
|
+
next if FieldTable[o[0]].nil?
|
|
181
181
|
next if o[3] == "host" && Sisimai::RFC1123.is_internethost(o[2]) == false
|
|
182
182
|
v[FieldTable[o[0]]] = o[2]
|
|
183
183
|
|
|
184
|
-
next
|
|
184
|
+
next if f != 1
|
|
185
185
|
permessage[FieldTable[o[0]]] = o[2]
|
|
186
186
|
end
|
|
187
187
|
else
|
|
188
188
|
# Check that the line is a continued line of the value of Diagnostic-Code: field or not
|
|
189
189
|
if e.start_with?("X-") && e.include?(": ")
|
|
190
190
|
# This line is a MTA-Specific fields begins with "X-"
|
|
191
|
-
next
|
|
191
|
+
next if Sisimai::RFC3464::ThirdParty.is3rdparty(e) == false
|
|
192
192
|
cv = Sisimai::RFC3464::ThirdParty.xfield(e)
|
|
193
193
|
|
|
194
194
|
if cv.size > 0 && FieldTable[cv[0].downcase] == nil
|
|
@@ -197,7 +197,7 @@ module Sisimai
|
|
|
197
197
|
v["reason"] = cv[4][p1 + 1, cv[4].size] if cv[4].start_with?("reason:")
|
|
198
198
|
else
|
|
199
199
|
# Set the value picked from "X-*" field to $dscontents when the current value is empty
|
|
200
|
-
z = FieldTable[cv[0].downcase]; next
|
|
200
|
+
z = FieldTable[cv[0].downcase]; next if z.nil?
|
|
201
201
|
v[z] ||= cv[2]
|
|
202
202
|
end
|
|
203
203
|
else
|
|
@@ -211,7 +211,7 @@ module Sisimai
|
|
|
211
211
|
|
|
212
212
|
# Diagnostic-Code: SMTP; 550-5.7.26 The MAIL FROM domain [email.example.jp]
|
|
213
213
|
# has an SPF record with a hard fail
|
|
214
|
-
next
|
|
214
|
+
next if e.start_with?(" ") == false
|
|
215
215
|
v["diagnosis"] += " #{Sisimai::String.sweep(e)}"
|
|
216
216
|
end
|
|
217
217
|
end
|
data/lib/sisimai/rfc3834.rb
CHANGED
|
@@ -28,6 +28,9 @@ module Sisimai
|
|
|
28
28
|
)
|
|
29
29
|
[ ]*(.+)\z
|
|
30
30
|
}x.freeze
|
|
31
|
+
Suspending = [
|
|
32
|
+
["this email inbox", " is no longer in use."],
|
|
33
|
+
].freeze
|
|
31
34
|
|
|
32
35
|
# Detect auto reply message as RFC3834
|
|
33
36
|
# @param [Hash] mhead Message headers of a bounce email
|
|
@@ -41,15 +44,14 @@ module Sisimai
|
|
|
41
44
|
|
|
42
45
|
LowerLabel.each do |e|
|
|
43
46
|
# Set lower-cased value of each header related to auto-response
|
|
44
|
-
next
|
|
47
|
+
next if mhead.has_key?(e) == false
|
|
45
48
|
lower[e] = mhead[e].downcase
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
# DETECT_EXCLUSION_MESSAGE
|
|
49
52
|
DoNotParse.each_key do |e|
|
|
50
53
|
# Exclude message from root@
|
|
51
|
-
next
|
|
52
|
-
next unless DoNotParse[e].any? { |a| lower[e].include?(a) }
|
|
54
|
+
next if lower[e].nil? || DoNotParse[e].none? { |a| lower[e].include?(a) }
|
|
53
55
|
leave = 1
|
|
54
56
|
break
|
|
55
57
|
end
|
|
@@ -58,8 +60,7 @@ module Sisimai
|
|
|
58
60
|
# DETECT_AUTO_REPLY_MESSAGE0
|
|
59
61
|
AutoReply0.each_key do |e|
|
|
60
62
|
# RFC3834 Auto-Submitted and other headers
|
|
61
|
-
next
|
|
62
|
-
next unless AutoReply0[e].any? { |a| lower[e].include?(a) }
|
|
63
|
+
next if lower[e].nil? || AutoReply0[e].none? { |a| lower[e].include?(a) }
|
|
63
64
|
match += 1
|
|
64
65
|
break
|
|
65
66
|
end
|
|
@@ -78,7 +79,7 @@ module Sisimai
|
|
|
78
79
|
# RECIPIENT_ADDRESS
|
|
79
80
|
%w[from return-path].each do |e|
|
|
80
81
|
# Try to get the address of the recipient
|
|
81
|
-
next
|
|
82
|
+
next if mhead[e].nil?
|
|
82
83
|
v['recipient'] = mhead[e]
|
|
83
84
|
break
|
|
84
85
|
end
|
|
@@ -88,12 +89,12 @@ module Sisimai
|
|
|
88
89
|
v['recipient'] = Sisimai::Address.s3s4(v['recipient'])
|
|
89
90
|
recipients += 1
|
|
90
91
|
end
|
|
91
|
-
return nil
|
|
92
|
+
return nil if recipients == 0
|
|
92
93
|
|
|
93
94
|
if mhead['content-type']
|
|
94
95
|
# Get the boundary string and set regular expression for matching with the boundary string.
|
|
95
96
|
q = Sisimai::RFC2045.boundary(mhead['content-type'], 0) || ''
|
|
96
|
-
MarkingsOf[:boundary] = q
|
|
97
|
+
MarkingsOf[:boundary] = q if q.empty? == false
|
|
97
98
|
end
|
|
98
99
|
|
|
99
100
|
# MESSAGE_BODY: Get the vacation message
|
|
@@ -117,6 +118,15 @@ module Sisimai
|
|
|
117
118
|
v['diagnosis'] ||= mhead['subject']
|
|
118
119
|
v['diagnosis'] = Sisimai::String.sweep(v['diagnosis'])
|
|
119
120
|
v['reason'] = 'vacation'
|
|
121
|
+
|
|
122
|
+
cv = v['diagnosis'].downcase
|
|
123
|
+
Suspending.each do |e|
|
|
124
|
+
# Check that the auto-replied message indicates the "Suspend" reason or not.
|
|
125
|
+
next unless Sisimai::String.aligned(cv, e)
|
|
126
|
+
v['reason'] = 'suspend'
|
|
127
|
+
break
|
|
128
|
+
end
|
|
129
|
+
|
|
120
130
|
v['date'] = mhead['date']
|
|
121
131
|
v['status'] = ''
|
|
122
132
|
|
data/lib/sisimai/rfc5322.rb
CHANGED
|
@@ -36,7 +36,7 @@ module Sisimai
|
|
|
36
36
|
# @param [String] argv1 Received header
|
|
37
37
|
# @return [Array] Received header as a structured data
|
|
38
38
|
def received(argv1)
|
|
39
|
-
return []
|
|
39
|
+
return [] if argv1.is_a?(::String) == false
|
|
40
40
|
return [] if argv1.include?(' invoked by uid') || argv1.include?(' invoked from network')
|
|
41
41
|
|
|
42
42
|
# - https://datatracker.ietf.org/doc/html/rfc5322
|
|
@@ -63,14 +63,14 @@ module Sisimai
|
|
|
63
63
|
recvd.each do |e|
|
|
64
64
|
# Look up each label defined in "label" from Received header
|
|
65
65
|
index += 1
|
|
66
|
-
break
|
|
67
|
-
next
|
|
66
|
+
break if index >= range; f = e.downcase
|
|
67
|
+
next if label.none? { |a| f == a }
|
|
68
68
|
token[f] = recvd[index + 1] || next
|
|
69
69
|
token[f] = token[f].downcase.delete('();')
|
|
70
70
|
|
|
71
|
-
next
|
|
72
|
-
break
|
|
73
|
-
next
|
|
71
|
+
next if f != 'from'
|
|
72
|
+
break if index + 2 >= range
|
|
73
|
+
next if recvd[index + 2].start_with?('(') == false
|
|
74
74
|
|
|
75
75
|
# Get and keep a hostname in the comment as follows:
|
|
76
76
|
# from mx1.example.com (c213502.kyoto.example.ne.jp [192.0.2.135]) by mx.example.jp (V8/cf)
|
|
@@ -90,7 +90,7 @@ module Sisimai
|
|
|
90
90
|
|
|
91
91
|
# The 2nd element after the current element is a continuation of the current element.
|
|
92
92
|
# such as "(c213502.kyoto.example.ne.jp", "[192.0.2.135])"
|
|
93
|
-
break
|
|
93
|
+
break if index + 3 >= range
|
|
94
94
|
other << recvd[index + 3].delete('();')
|
|
95
95
|
end
|
|
96
96
|
|
|
@@ -173,7 +173,7 @@ module Sisimai
|
|
|
173
173
|
|
|
174
174
|
cutby.each do |e|
|
|
175
175
|
# Find a boundary string(2nd argument) from the 1st argument
|
|
176
|
-
positionor = email.index(e); next
|
|
176
|
+
positionor = email.index(e); next if positionor.nil?
|
|
177
177
|
boundaryor = e
|
|
178
178
|
break
|
|
179
179
|
end
|
|
@@ -200,7 +200,7 @@ module Sisimai
|
|
|
200
200
|
# Remove text after the first blank line: \n\n when "keeps" is false
|
|
201
201
|
latterpart = latterpart[0, latterpart.index("\n\n")] if latterpart.include?("\n\n")
|
|
202
202
|
end
|
|
203
|
-
latterpart << "\n"
|
|
203
|
+
latterpart << "\n" if latterpart.end_with?("\n") == false
|
|
204
204
|
end
|
|
205
205
|
|
|
206
206
|
return [formerpart, latterpart]
|
data/lib/sisimai/rfc791.rb
CHANGED
|
@@ -31,13 +31,13 @@ module Sisimai
|
|
|
31
31
|
ipv4a = []
|
|
32
32
|
%w|( ) [ ] ,|.each do |e|
|
|
33
33
|
# Rewrite: "mx.example.jp[192.0.2.1]" => "mx.example.jp 192.0.2.1"
|
|
34
|
-
p0 = given.index(e); next
|
|
34
|
+
p0 = given.index(e); next if p0.nil?
|
|
35
35
|
given[p0, 1] = ' '
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
given.split(' ').each do |e|
|
|
39
39
|
# Find string including an IPv4 address
|
|
40
|
-
next
|
|
40
|
+
next if e.index('.').nil? # IPv4 address must include "." character
|
|
41
41
|
|
|
42
42
|
lx = e.size; next if lx < 7 || lx > 17 # 0.0.0.0 = 7, [255.255.255.255] = 17
|
|
43
43
|
cu = 0 # Cursor for seeking each octet of an IPv4 address
|
data/lib/sisimai/rhost/aol.rb
CHANGED
|
@@ -14,6 +14,9 @@ module Sisimai
|
|
|
14
14
|
# @param [Sisimai::Fact] argvs Decoded email object
|
|
15
15
|
# @return [String] The bounce reason for Aol
|
|
16
16
|
# @since v5.2.0
|
|
17
|
+
# @see
|
|
18
|
+
# - Y!Sender Hub/SMTP Error Codes: https://senders.yahooinc.com/smtp-error-codes/
|
|
19
|
+
# - The MX record of Aol points "mx-aol.mail.gm0.yahoodns.net".
|
|
17
20
|
def find(argvs)
|
|
18
21
|
return "" if argvs["diagnosticcode"].empty?
|
|
19
22
|
issuedcode = argvs["diagnosticcode"]
|
|
@@ -21,7 +24,7 @@ module Sisimai
|
|
|
21
24
|
|
|
22
25
|
MessagesOf.each_key do |e|
|
|
23
26
|
# Try to match the error message with message patterns defined in $MessagesOf
|
|
24
|
-
next
|
|
27
|
+
next if MessagesOf[e].none? { |a| issuedcode.include?(a) }
|
|
25
28
|
reasontext = e
|
|
26
29
|
break
|
|
27
30
|
end
|
data/lib/sisimai/rhost/apple.rb
CHANGED
|
@@ -53,10 +53,14 @@ module Sisimai
|
|
|
53
53
|
# Try again later - https://support.apple.com/en-us/HT204137
|
|
54
54
|
'due to excessive volume',
|
|
55
55
|
],
|
|
56
|
+
'suspend' => [
|
|
57
|
+
# - https://support.apple.com/guide/icloud/stop-using-or-reactivate-addresses-mm3adb030cbf/icloud
|
|
58
|
+
# - 550 5.1.1 <****@icloud.com>: inactive email address (in reply to RCPT TO command)
|
|
59
|
+
"inactive email address",
|
|
60
|
+
],
|
|
56
61
|
'userunknown' => [
|
|
57
62
|
# - 550 5.1.1 <****@icloud.com>: inactive email address (in reply to RCPT TO command)
|
|
58
63
|
# - 550 5.1.1 unknown or illegal alias: ****@icloud.com
|
|
59
|
-
'inactive email address',
|
|
60
64
|
'user does not exist',
|
|
61
65
|
'unknown or illegal alias',
|
|
62
66
|
],
|
|
@@ -65,20 +69,20 @@ module Sisimai
|
|
|
65
69
|
# Detect bounce reason from Apple iCloud Mail
|
|
66
70
|
# @param [Sisimai::Fact] argvs Decoded email object
|
|
67
71
|
# @return [String] The bounce reason for Apple
|
|
68
|
-
# @see
|
|
69
|
-
#
|
|
70
|
-
#
|
|
72
|
+
# @see
|
|
73
|
+
# - Postmaster information for iCloud Mail: https://support.apple.com/en-us/102322
|
|
74
|
+
# - https://www.postmastery.com/icloud-postmastery-page/
|
|
75
|
+
# - https://smtpfieldmanual.com/provider/apple
|
|
71
76
|
# @since v5.1.0
|
|
72
77
|
def find(argvs)
|
|
73
|
-
return ''
|
|
74
|
-
return '' unless argvs['diagnosticcode'].size > 0
|
|
78
|
+
return '' if argvs.nil? || argvs['diagnosticcode'].size == 0
|
|
75
79
|
|
|
76
80
|
issuedcode = argvs['diagnosticcode'].downcase
|
|
77
81
|
reasontext = ''
|
|
78
82
|
|
|
79
83
|
MessagesOf.each_key do |e|
|
|
80
84
|
MessagesOf[e].each do |f|
|
|
81
|
-
next
|
|
85
|
+
next if issuedcode.include?(f) == false
|
|
82
86
|
reasontext = e
|
|
83
87
|
break
|
|
84
88
|
end
|
data/lib/sisimai/rhost/cox.rb
CHANGED
|
@@ -61,7 +61,7 @@ module Sisimai
|
|
|
61
61
|
'IPBL0101' => 'blocked', # The sending IP is in the Spamhaus Zen and Invaluement ivmSIP DNSBLs.
|
|
62
62
|
'IPBL0110' => 'blocked', # The sending IP is in the Return Path and Invaluement ivmSIP DNSBLs.
|
|
63
63
|
'IPBL0111' => 'blocked', # The sending IP is in the Spamhaus Zen, Return Path and Invaluement ivmSIP DNSBLs.
|
|
64
|
-
'IPBL1000' => 'blocked', # The sending IP address is listed on a CSI blacklist.
|
|
64
|
+
'IPBL1000' => 'blocked', # The sending IP address is listed on a CSI blacklist.
|
|
65
65
|
'IPBL1001' => 'blocked', # The sending IP is listed in the Cloudmark CSI and Spamhaus Zen DNSBLs.
|
|
66
66
|
'IPBL1010' => 'blocked', # The sending IP is listed in the Cloudmark CSI and Return Path DNSBLs.
|
|
67
67
|
'IPBL1011' => 'blocked', # The sending IP is in the Cloudmark CSI, Spamhaus Zen and Return Path DNSBLs.
|
|
@@ -69,12 +69,12 @@ module Sisimai
|
|
|
69
69
|
'IPBL1101' => 'blocked', # The sending IP is in the Cloudmark CSI, Spamhaus Zen and Invaluement IVMsip DNSBLs.
|
|
70
70
|
'IPBL1110' => 'blocked', # The sending IP is in the Cloudmark CSI, Return Path and Invaluement ivmSIP DNSBLs.
|
|
71
71
|
'IPBL1111' => 'blocked', # The sending IP is in the Cloudmark CSI, Spamhaus Zen, Return Path and Invaluement ivmSIP DNSBLs.
|
|
72
|
-
'IPBL00001' => 'blocked', # The sending IP address is listed on a Spamhaus blacklist.
|
|
72
|
+
'IPBL00001' => 'blocked', # The sending IP address is listed on a Spamhaus blacklist.
|
|
73
73
|
|
|
74
74
|
'URLBL011' => 'spamdetected', # A URL within the body of the message was found on blocklists SURBL and Spamhaus DBL.
|
|
75
75
|
'URLBL101' => 'spamdetected', # A URL within the body of the message was found on blocklists SURBL and ivmURI.
|
|
76
76
|
'URLBL110' => 'spamdetected', # A URL within the body of the message was found on blocklists Spamhaus DBL and ivmURI.
|
|
77
|
-
'URLBL1001' => 'spamdetected', # The URL is listed on a Spamhaus blacklist.
|
|
77
|
+
'URLBL1001' => 'spamdetected', # The URL is listed on a Spamhaus blacklist.
|
|
78
78
|
}.freeze
|
|
79
79
|
MessagesOf = {
|
|
80
80
|
'blocked' => [
|
|
@@ -82,6 +82,8 @@ module Sisimai
|
|
|
82
82
|
# a three-hour block of the client's IP address.
|
|
83
83
|
# - The sending IP address has exceeded the threshold of invalid recipients and has
|
|
84
84
|
# been blocked.
|
|
85
|
+
# - Cox enforces various rate limits to protect our platform. The sending IP address
|
|
86
|
+
# has exceeded one of these rate limits and has been temporarily blocked.
|
|
85
87
|
'cox too many bad commands from',
|
|
86
88
|
'too many invalid recipients',
|
|
87
89
|
],
|
|
@@ -127,7 +129,9 @@ module Sisimai
|
|
|
127
129
|
# Detect bounce reason from https://cox.com/
|
|
128
130
|
# @param [Sisimai::Fact] argvs Decoded email object
|
|
129
131
|
# @return [String, Nil] The bounce reason at Cox
|
|
130
|
-
# @see
|
|
132
|
+
# @see
|
|
133
|
+
# - Email Error Codes: https://www.cox.com/business/support/email-error-codes.html
|
|
134
|
+
# - Feedback Loop Service https://www.cox.com/business/support/feedback-loop-service.html
|
|
131
135
|
# @since v4.25.8
|
|
132
136
|
def find(argvs)
|
|
133
137
|
return "" if argvs["diagnosticcode"].empty?
|
|
@@ -145,7 +149,7 @@ module Sisimai
|
|
|
145
149
|
# The error code was not found in ErrorCodes
|
|
146
150
|
MessagesOf.each_key do |e|
|
|
147
151
|
# Try to find with each error message defined in MessagesOf
|
|
148
|
-
next
|
|
152
|
+
next if MessagesOf[e].none? { |a| issuedcode.include?(a) }
|
|
149
153
|
reasontext = e
|
|
150
154
|
break
|
|
151
155
|
end
|
|
@@ -27,6 +27,9 @@ module Sisimai
|
|
|
27
27
|
"RCP-P2", # The attempted recipient's preferences prevent messages from being delivered.
|
|
28
28
|
"RCP-P3", # The attempted recipient's privacy settings blocked the delivery.
|
|
29
29
|
],
|
|
30
|
+
"mailboxfull" => [
|
|
31
|
+
"INT-P7", # The attempted recipient has exceeded their storage quota.
|
|
32
|
+
],
|
|
30
33
|
"mesgtoobig" => [
|
|
31
34
|
"MSG-P1", # The message exceeds Facebook's maximum allowed size.
|
|
32
35
|
"INT-P2", # The message exceeds Facebook's maximum allowed size.
|
|
@@ -34,6 +37,9 @@ module Sisimai
|
|
|
34
37
|
"notcompliantrfc" => [
|
|
35
38
|
"MSG-P3", # The message contains multiple instances of a header field that can only be present once.
|
|
36
39
|
],
|
|
40
|
+
"policyviolation" => [
|
|
41
|
+
"POL-P8", # The message does not comply with Facebook's abuse policies and will not be accepted.
|
|
42
|
+
],
|
|
37
43
|
"rejected" => [
|
|
38
44
|
"DNS-P1", # Your SMTP MAIL FROM domain does not exist.
|
|
39
45
|
"DNS-P2", # Your SMTP MAIL FROM domain does not have an MX record.
|
|
@@ -45,13 +51,13 @@ module Sisimai
|
|
|
45
51
|
],
|
|
46
52
|
"spamdetected" => [
|
|
47
53
|
"POL-P6", # The message contains a url that has been blocked by Facebook.
|
|
48
|
-
"POL-P7", # The message does not comply with Facebook's abuse policies and will not be accepted.
|
|
49
54
|
],
|
|
50
55
|
"suspend" => [
|
|
51
56
|
"RCP-T4", # The attempted recipient address is currently deactivated. The user may or may not reactivate it.
|
|
52
57
|
],
|
|
53
58
|
"systemerror" => [
|
|
54
|
-
"RCP-T1", # The attempted recipient address is not currently available due to an internal system issue.
|
|
59
|
+
"RCP-T1", # The attempted recipient address is not currently available due to an internal system issue.
|
|
60
|
+
"INT-Tx", # These codes indicate a temporary issue internal to Facebook's system.
|
|
55
61
|
],
|
|
56
62
|
"toomanyconn" => [
|
|
57
63
|
"CON-T1", # Facebook's mail server currently has too many connections open to allow another one.
|
|
@@ -85,7 +91,7 @@ module Sisimai
|
|
|
85
91
|
|
|
86
92
|
ErrorCodes.each_key do |e|
|
|
87
93
|
# The key is a bounce reason name
|
|
88
|
-
next
|
|
94
|
+
next if ErrorCodes[e].none? { |a| errorlabel == a }
|
|
89
95
|
reasontext = e
|
|
90
96
|
break
|
|
91
97
|
end
|