sisimai 5.4.1 → 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 +35 -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 +6 -6
- 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 +77 -26
- 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 +4 -3
- 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-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 +26 -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
|
@@ -16,106 +16,86 @@ module Sisimai
|
|
|
16
16
|
# @param [Sisimai::Fact] argvs Decoded email object
|
|
17
17
|
# @return [String] The bounce reason for docomo.ne.jp
|
|
18
18
|
def find(argvs)
|
|
19
|
-
return argvs['reason']
|
|
19
|
+
return argvs['reason'] if argvs['reason'].empty? == false
|
|
20
20
|
statuscode = argvs['deliverystatus'] || ''
|
|
21
21
|
thecommand = argvs['command'] || ''
|
|
22
22
|
esmtperror = argvs['diagnosticcode'].downcase || ''
|
|
23
|
-
reasontext = ''
|
|
24
23
|
|
|
25
24
|
# Check the value of Status: field, an SMTP Reply Code, and the SMTP Command
|
|
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
|
-
reasontext = 'filtered'
|
|
25
|
+
#
|
|
26
|
+
# ----- Transcript of session follows -----
|
|
27
|
+
# ... while talking to mfsmax.docomo.ne.jp.:
|
|
28
|
+
# >>> RCPT To:<***@docomo.ne.jp>
|
|
29
|
+
# <<< 550 Unknown user ***@docomo.ne.jp
|
|
30
|
+
# 550 5.1.1 <***@docomo.ne.jp>... User unknown
|
|
31
|
+
# >>> DATA
|
|
32
|
+
# <<< 503 Bad sequence of commands
|
|
33
|
+
# ---------------------------------------------------------------------------------------
|
|
34
|
+
# ----- The following addresses had permanent fatal errors -----
|
|
35
|
+
# <***@docomo.ne.jp>
|
|
36
|
+
# (reason: 550 Unknown user ***@docomo.ne.jp)
|
|
37
|
+
#
|
|
38
|
+
# ----- Transcript of session follows -----
|
|
39
|
+
# ... while talking to mfsmax.docomo.ne.jp.:
|
|
40
|
+
# >>> DATA
|
|
41
|
+
# <<< 550 Unknown user ***@docomo.ne.jp
|
|
42
|
+
# 554 5.0.0 Service unavailable
|
|
43
|
+
# ...
|
|
44
|
+
# Final-Recipient: RFC822; ***@docomo.ne.jp
|
|
45
|
+
# Action: failed
|
|
46
|
+
# Status: 5.2.0
|
|
47
|
+
return 'userunknown' if statuscode == '5.1.1' || statuscode == '5.0.911'
|
|
48
|
+
return 'filtered' if statuscode == '5.2.0'
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
MessagesOf.each_key do |e|
|
|
53
51
|
# The value of "Diagnostic-Code:" field is not empty
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
next unless MessagesOf[e].any? { |a| esmtperror.include?(a) }
|
|
62
|
-
reasontext = e
|
|
63
|
-
break
|
|
64
|
-
end
|
|
52
|
+
# - The key name is a bounce reason name
|
|
53
|
+
# - https://github.com/sisimai/go-sisimai/issues/64
|
|
54
|
+
# - After March 12, 2025, if an error message contains "550 Unknown user", the
|
|
55
|
+
# bounce reason will be definitively "userunknown". This is because NTT DOCOMO
|
|
56
|
+
# no longer rejects emails via SMTP for domain-specific rejection or specified
|
|
57
|
+
# reception filters.
|
|
58
|
+
return e if MessagesOf[e].any? { |a| esmtperror.include?(a) }
|
|
65
59
|
end
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
# Status: 5.0.0
|
|
96
|
-
# Remote-MTA: dns; mfsmax.docomo.ne.jp
|
|
97
|
-
# Diagnostic-Code: smtp; 550 Unknown user ***@docomo.ne.jp
|
|
98
|
-
reasontext = 'rejected'
|
|
99
|
-
|
|
100
|
-
else
|
|
101
|
-
# Rejected by other SMTP commands: AUTH, MAIL,
|
|
102
|
-
# もしもこのブロックを通過するNTTドコモからのエラーメッセージを見つけたら
|
|
103
|
-
# https://github.com/sisimai/rb-sisimai/issues からご連絡ねがいます。
|
|
104
|
-
#
|
|
105
|
-
# If you found a error message from mfsmax.docomo.ne.jp which passes this block,
|
|
106
|
-
# please open an issue at https://github.com/sisimai/rb-sisimai/issues .
|
|
107
|
-
end
|
|
108
|
-
else
|
|
109
|
-
# Status: field is neither 5.0.0 nor values defined in code above
|
|
110
|
-
# もしもこのブロックを通過するNTTドコモからのエラーメッセージを見つけたら
|
|
111
|
-
# https://github.com/sisimai/rb-sisimai/issues からご連絡ねがいます。
|
|
112
|
-
#
|
|
113
|
-
# If you found a error message from mfsmax.docomo.ne.jp which passes this block,
|
|
114
|
-
#
|
|
115
|
-
end
|
|
61
|
+
# A bounce reason did not decide from a status code, an error message.
|
|
62
|
+
if statuscode == '5.0.0'
|
|
63
|
+
# Your message to the following recipients cannot be delivered:
|
|
64
|
+
#
|
|
65
|
+
# <***@docomo.ne.jp>:
|
|
66
|
+
# mfsmax.docomo.ne.jp [203.138.181.112]:
|
|
67
|
+
# >>> RCPT TO:<***@docomo.ne.jp>
|
|
68
|
+
# <<< 550 Unknown user ***@docomo.ne.jp
|
|
69
|
+
# ...
|
|
70
|
+
#
|
|
71
|
+
# Final-Recipient: rfc822; ***@docomo.ne.jp
|
|
72
|
+
# Action: failed
|
|
73
|
+
# Status: 5.0.0
|
|
74
|
+
# Remote-MTA: dns; mfsmax.docomo.ne.jp [203.138.181.112]
|
|
75
|
+
# Diagnostic-Code: smtp; 550 Unknown user ***@docomo.ne.jp
|
|
76
|
+
# -------------------------------------------------------------------------------------
|
|
77
|
+
# <***@docomo.ne.jp>: host mfsmax.docomo.ne.jp[203.138.181.240] said:
|
|
78
|
+
# 550 Unknown user ***@docomo.ne.jp (in reply to end of DATA
|
|
79
|
+
# command)
|
|
80
|
+
# ...
|
|
81
|
+
# Final-Recipient: rfc822; ***@docomo.ne.jp
|
|
82
|
+
# Original-Recipient: rfc822;***@docomo.ne.jp
|
|
83
|
+
# Action: failed
|
|
84
|
+
# Status: 5.0.0
|
|
85
|
+
# Remote-MTA: dns; mfsmax.docomo.ne.jp
|
|
86
|
+
# Diagnostic-Code: smtp; 550 Unknown user ***@docomo.ne.jp
|
|
87
|
+
return 'userunknown' if thecommand == 'RCPT'
|
|
88
|
+
return 'rejected' if thecommand == 'DATA'
|
|
116
89
|
end
|
|
117
90
|
|
|
118
|
-
|
|
91
|
+
# 1. Rejected by other SMTP commands: AUTH, MAIL,
|
|
92
|
+
# 2. Status: field is neither 5.0.0 nor values defined in code above
|
|
93
|
+
# もしもこのブロックを通過するNTTドコモからのエラーメッセージを見つけたら
|
|
94
|
+
# https://github.com/sisimai/rb-sisimai/issues からご連絡ねがいます。
|
|
95
|
+
#
|
|
96
|
+
# If you found a error message from mfsmax.docomo.ne.jp which passes this block,
|
|
97
|
+
# please open an issue at https://github.com/sisimai/rb-sisimai/issues .
|
|
98
|
+
return ""
|
|
119
99
|
end
|
|
120
100
|
|
|
121
101
|
end
|
|
@@ -21,7 +21,7 @@ module Sisimai
|
|
|
21
21
|
|
|
22
22
|
MessagesOf.each_key do |e|
|
|
23
23
|
# Try to match the error message with message patterns defined in $MessagesOf
|
|
24
|
-
next
|
|
24
|
+
next if MessagesOf[e].none? { |a| issuedcode.include?(a) }
|
|
25
25
|
reasontext = e
|
|
26
26
|
break
|
|
27
27
|
end
|
|
@@ -101,7 +101,7 @@ module Sisimai
|
|
|
101
101
|
# @return [String, Nil] The bounce reason at Spectrum
|
|
102
102
|
# @since v4.25.8
|
|
103
103
|
def find(argvs)
|
|
104
|
-
return argvs['reason']
|
|
104
|
+
return argvs['reason'] if argvs['reason'].empty? == false
|
|
105
105
|
issuedcode = argvs['diagnosticcode']
|
|
106
106
|
reasontext = ''
|
|
107
107
|
codenumber = if cv = issuedcode.match(/AUP#[-A-Za-z]*(\d{4})/) then cv[1].to_i else 0 end
|
|
@@ -22,12 +22,13 @@ module Sisimai
|
|
|
22
22
|
'suspected spam', # https://service.mail.qq.com/detail/122/71
|
|
23
23
|
'mail is rejected by recipients', # https://service.mail.qq.com/detail/122/92
|
|
24
24
|
],
|
|
25
|
-
'
|
|
25
|
+
'spamdetected' => [
|
|
26
26
|
'spam is embedded in the email', # https://service.mail.qq.com/detail/122/59
|
|
27
27
|
'mail content denied', # https://service.mail.qq.com/detail/122/171
|
|
28
28
|
],
|
|
29
29
|
'speeding' => [
|
|
30
|
-
'mailbox unavailable or access denined',
|
|
30
|
+
'mailbox unavailable or access denined', # https://service.mail.qq.com/detail/122/166
|
|
31
|
+
"frequency of receiving messages is limited", # https://service.mail.qq.com/detail/122/1011
|
|
31
32
|
],
|
|
32
33
|
'suspend' => [
|
|
33
34
|
'is a deactivated mailbox', # http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=20022&&no=1000742
|
|
@@ -50,13 +51,13 @@ module Sisimai
|
|
|
50
51
|
# @param [Sisimai::Fact] argvs Decoded email object
|
|
51
52
|
# @return [String] The bounce reason at Tencent QQ
|
|
52
53
|
def find(argvs)
|
|
53
|
-
return argvs['reason']
|
|
54
|
+
return argvs['reason'] if argvs['reason'].empty? == false
|
|
54
55
|
issuedcode = argvs['diagnosticcode'].downcase
|
|
55
56
|
reasontext = ''
|
|
56
57
|
|
|
57
58
|
MessagesOf.each_key do |e|
|
|
58
59
|
MessagesOf[e].each do |f|
|
|
59
|
-
next
|
|
60
|
+
next if issuedcode.include?(f) == false
|
|
60
61
|
reasontext = e
|
|
61
62
|
break
|
|
62
63
|
end
|
|
@@ -88,13 +88,13 @@ module Sisimai
|
|
|
88
88
|
# https://www.postmastery.com/yahoo-postmaster/
|
|
89
89
|
# @since v5.1.0
|
|
90
90
|
def find(argvs)
|
|
91
|
-
return argvs['reason']
|
|
91
|
+
return argvs['reason'] if argvs['reason'].empty? == false
|
|
92
92
|
issuedcode = argvs['diagnosticcode'].downcase
|
|
93
93
|
reasontext = ''
|
|
94
94
|
|
|
95
95
|
MessagesOf.each_key do |e|
|
|
96
96
|
MessagesOf[e].each do |f|
|
|
97
|
-
next
|
|
97
|
+
next if issuedcode.include?(f) == false
|
|
98
98
|
reasontext = e
|
|
99
99
|
break
|
|
100
100
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Sisimai
|
|
2
|
+
module Rhost
|
|
3
|
+
# Sisimai::Rhost detects the bounce reason from the content of Sisimai::Fact object as an argument
|
|
4
|
+
# of find() method when the value of "destination" of the object is ".zoho.com" or ".zoho.eu".
|
|
5
|
+
# This class is called only Sisimai::Fact class.
|
|
6
|
+
module Zoho
|
|
7
|
+
class << self
|
|
8
|
+
MessagesOf = {
|
|
9
|
+
'authfailure' => [
|
|
10
|
+
# - <*******@zoho.com>: host smtpin.zoho.com[204.141.33.23] said: 550 5.7.1 Email
|
|
11
|
+
# rejected per DMARC policy for zoho.com
|
|
12
|
+
"Email rejected per DMARC policy",
|
|
13
|
+
],
|
|
14
|
+
'blocked' => [
|
|
15
|
+
# - mx.zoho.com[204.141.33.44]:25, delay=1202, delays=1200/0/0.91/0.30, dsn=4.7.1,
|
|
16
|
+
# status=deferred (host mx.zoho.com[204.141.33.44] said:
|
|
17
|
+
# 451 4.7.1 Greylisted, try again after some time (in reply to RCPT TO command))
|
|
18
|
+
"Greylisted, try again after some time",
|
|
19
|
+
],
|
|
20
|
+
'rejected' => [
|
|
21
|
+
# - <*******@zoho.com>: host smtpin.zoho.com[204.141.33.23] said: 554 5.7.1 Email
|
|
22
|
+
# cannot be delivered. Reason: Email flagged as Spam. (in reply to RCPT TO command)
|
|
23
|
+
# - <***@zoho.com>: host mx.zoho.com[136.143.183.44] said: 541 5.4.1 Mail rejected
|
|
24
|
+
# by destination domain (in reply to RCPT TO command)
|
|
25
|
+
"Email cannot be delivered. Reason: Email flagged as Spam",
|
|
26
|
+
"Mail rejected by destination domain",
|
|
27
|
+
],
|
|
28
|
+
'policyviolation' => [
|
|
29
|
+
# - <*******@zoho.com>: host smtpin.zoho.com[204.141.33.23] said: 554 5.7.7 Email
|
|
30
|
+
# policy violation detected (in reply to end of DATA command)
|
|
31
|
+
"Email policy violation detected",
|
|
32
|
+
"Mailbox delivery restricted by policy error",
|
|
33
|
+
],
|
|
34
|
+
'systemerror' => [
|
|
35
|
+
# - https://github.com/zoho/zohodesk-oas/blob/main/v1.0/EmailFailureAlert.json#L168
|
|
36
|
+
# 452 4.3.1 Temporary System Error
|
|
37
|
+
"Temporary System Error",
|
|
38
|
+
],
|
|
39
|
+
'userunknown' => [
|
|
40
|
+
# - <*******@zoho.com>: host smtpin.zoho.com[204.141.33.23] said:
|
|
41
|
+
# 550 5.1.1 User does not exist - <***@zoho.com> (in reply to RCPT TO command)
|
|
42
|
+
# - 552 5.1.1 <****@zoho.com> Mailbox delivery failure policy error
|
|
43
|
+
"User does not exist",
|
|
44
|
+
],
|
|
45
|
+
'virusdetected' => [
|
|
46
|
+
# - 552 5.7.1 virus **** detected by Zoho Mail
|
|
47
|
+
" detected by Zoho Mail",
|
|
48
|
+
],
|
|
49
|
+
}.freeze
|
|
50
|
+
|
|
51
|
+
# Detect bounce reason from Apple iCloud Mail
|
|
52
|
+
# @param [Sisimai::Fact] argvs Decoded email object
|
|
53
|
+
# @return [String] The bounce reason for Apple
|
|
54
|
+
# @see
|
|
55
|
+
# @since v5.5.0
|
|
56
|
+
# - Zoho Mail: https://www.zoho.com/mail/
|
|
57
|
+
# - Reasons an email is marked as Spam: https://www.zoho.com/mail/help/spam-reason.html
|
|
58
|
+
# - https://github.com/zoho/zohodesk-oas/blob/main/v1.0/EmailFailureAlert.json
|
|
59
|
+
# - Zoho SMTP Error Codes | SMTP Field Manual: https://smtpfieldmanual.com/provider/zoho
|
|
60
|
+
def find(argvs)
|
|
61
|
+
return '' if argvs.nil? || argvs['diagnosticcode'].size == 0
|
|
62
|
+
MessagesOf.each_key do |e|
|
|
63
|
+
return e if MessagesOf[e].any? { |a| argvs['diagnosticcode'].include?(a) }
|
|
64
|
+
end
|
|
65
|
+
return ''
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
data/lib/sisimai/rhost.rb
CHANGED
|
@@ -24,6 +24,7 @@ module Sisimai
|
|
|
24
24
|
"Spectrum" => ["charter.net"],
|
|
25
25
|
"Tencent" => [".qq.com"],
|
|
26
26
|
"YahooInc" => [".yahoodns.net"],
|
|
27
|
+
"Zoho" => [".zoho.com", ".zoho.eu"],
|
|
27
28
|
}.freeze
|
|
28
29
|
|
|
29
30
|
# Returns the rhost class name
|
|
@@ -44,14 +45,14 @@ module Sisimai
|
|
|
44
45
|
# 3. lhost: local MTA hostname
|
|
45
46
|
RhostClass.each_key do |e|
|
|
46
47
|
# Try to match the domain part of the recipient address with each value of RhostClass
|
|
47
|
-
next
|
|
48
|
+
next if RhostClass[e].none? { |a| a.end_with?(domainpart) }
|
|
48
49
|
rhostclass = e
|
|
49
50
|
throw :FINDRHOST
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
RhostClass.each_key do |e|
|
|
53
54
|
# Try to match the remote host with each value of RhostClass
|
|
54
|
-
next
|
|
55
|
+
next if RhostClass[e].none? { |a| remotehost.end_with?(a) }
|
|
55
56
|
rhostclass = e
|
|
56
57
|
throw :FINDRHOST
|
|
57
58
|
end
|
|
@@ -59,7 +60,7 @@ module Sisimai
|
|
|
59
60
|
# Neither the remote host nor the destination did not matched with any value of RhostClass
|
|
60
61
|
RhostClass.each_key do |e|
|
|
61
62
|
# Try to match the client host with each value of RhostClass
|
|
62
|
-
next
|
|
63
|
+
next if RhostClass[e].none? { |a| clienthost.end_with?(a) }
|
|
63
64
|
rhostclass = e
|
|
64
65
|
throw :FINDRHOST
|
|
65
66
|
end
|
data/lib/sisimai/smtp/command.rb
CHANGED
|
@@ -28,7 +28,7 @@ module Sisimai
|
|
|
28
28
|
# @return [String] An SMTP command
|
|
29
29
|
# @since v5.0.0
|
|
30
30
|
def find(argv0 = '')
|
|
31
|
-
return ""
|
|
31
|
+
return "" if Sisimai::SMTP::Command.test(argv0) == false
|
|
32
32
|
|
|
33
33
|
issuedcode = " " + argv0.downcase + " "
|
|
34
34
|
commandmap = {"STAR" => "STARTTLS", "XFOR" => "XFORWARD"}
|
|
@@ -36,7 +36,7 @@ module Sisimai
|
|
|
36
36
|
|
|
37
37
|
Detectable.each do |e|
|
|
38
38
|
# Find an SMTP command from the given string
|
|
39
|
-
p0 = argv0.index(e); next
|
|
39
|
+
p0 = argv0.index(e); next if p0.nil?
|
|
40
40
|
if e.include?(" ") == false
|
|
41
41
|
# For example, "RCPT T" does not appear in an email address or a domain name
|
|
42
42
|
cx = true; while true do
|
data/lib/sisimai/smtp/reply.rb
CHANGED
|
@@ -113,18 +113,25 @@ module Sisimai
|
|
|
113
113
|
Associated = {
|
|
114
114
|
"422" => ["AUTH", "4.7.12", "securityerror"], # RFC5238
|
|
115
115
|
"432" => ["AUTH", "4.7.12", "securityerror"], # RFC4954, RFC5321
|
|
116
|
+
"451" => ["", "", "systemerror"], # RFC2465, RFC5321
|
|
117
|
+
"452" => ["", "", "systemfull"], # RFC5321
|
|
118
|
+
"454" => ["AUTH", "4.7.0", "securityerror"], # RFC3027, RFC4954
|
|
119
|
+
"455" => ["", "", "syntaxerror"], # RFC5321
|
|
116
120
|
"500" => ["", "", "syntaxerror"], # RFC5321
|
|
117
121
|
"501" => ["", "", "syntaxerror"], # RFC5321
|
|
118
122
|
"502" => ["", "", "syntaxerror"], # RFC5321
|
|
119
123
|
"503" => ["", "", "syntaxerror"], # RFC5321
|
|
120
124
|
"504" => ["", "", "syntaxerror"], # RFC5321
|
|
121
125
|
"521" => ["CONN", "", "notaccept"], # RFC7504
|
|
122
|
-
"523" => ["AUTH", "",
|
|
123
|
-
"524" => ["AUTH", "",
|
|
124
|
-
"525" => ["AUTH", "",
|
|
126
|
+
"523" => ["AUTH", "5.7.10", "securityerror"], # RFC5248
|
|
127
|
+
"524" => ["AUTH", "5.7.11", "securityerror"], # RFC5248
|
|
128
|
+
"525" => ["AUTH", "5.7.13", "securityerror"], # RFC5248
|
|
125
129
|
"534" => ["AUTH", "5.7.9", "securityerror"], # RFC4954, RFC5248
|
|
126
130
|
"535" => ["AUTH", "5.7.8", "securityerror"], # RFC4954, RFC5248
|
|
127
131
|
"538" => ["AUTH", "5.7.11", "securityerror"], # RFC4954, RFC5248
|
|
132
|
+
"551" => ["", "", "hasmoved"], # RFC5321, RFC5336, RFC6531
|
|
133
|
+
"552" => ["", "", "mailboxfull"], # RFC5321
|
|
134
|
+
"555" => ["", "", "syntaxerror"], # RFC5321
|
|
128
135
|
"556" => ["RCPT", "", "notaccept"], # RFC7504
|
|
129
136
|
}.freeze
|
|
130
137
|
|
|
@@ -153,7 +160,7 @@ module Sisimai
|
|
|
153
160
|
|
|
154
161
|
if first == 3
|
|
155
162
|
# 3yz
|
|
156
|
-
return false
|
|
163
|
+
return false if reply != 334 && reply != 354
|
|
157
164
|
return true
|
|
158
165
|
end
|
|
159
166
|
|
data/lib/sisimai/smtp/status.rb
CHANGED
|
@@ -554,7 +554,7 @@ module Sisimai
|
|
|
554
554
|
'5.4.0' => 'networkerror', # Other or undefined network or routing status
|
|
555
555
|
'5.4.3' => 'systemerror', # Directory server failure
|
|
556
556
|
'5.4.4' => 'hostunknown', # Unable to route
|
|
557
|
-
'5.5.2' => '
|
|
557
|
+
'5.5.2' => 'systemerror', # If the server cannot BASE64 decode any client response (AUTH)
|
|
558
558
|
'5.5.3' => 'toomanyconn', # Too many recipients
|
|
559
559
|
'5.5.4' => 'systemerror', # Invalid command arguments
|
|
560
560
|
'5.5.5' => 'systemerror', # Wrong protocol version
|
|
@@ -692,7 +692,7 @@ module Sisimai
|
|
|
692
692
|
# @return [String] Reason name or an empty string
|
|
693
693
|
# @see code
|
|
694
694
|
def name(argv1 = nil)
|
|
695
|
-
return ""
|
|
695
|
+
return "" if Sisimai::SMTP::Status.test(argv1.to_s) == false
|
|
696
696
|
return StandardCode[argv1] || ""
|
|
697
697
|
end
|
|
698
698
|
|
|
@@ -706,7 +706,7 @@ module Sisimai
|
|
|
706
706
|
|
|
707
707
|
token = []
|
|
708
708
|
argv1.split('.').each { |e| token << e.to_i }
|
|
709
|
-
return false
|
|
709
|
+
return false if token.size != 3
|
|
710
710
|
return false if token[0] < 2 || token[0] == 3 || token[0] > 5
|
|
711
711
|
return false if token[1] < 0 || token[1] > 7
|
|
712
712
|
return false if token[2] < 0
|
|
@@ -755,7 +755,7 @@ module Sisimai
|
|
|
755
755
|
|
|
756
756
|
lookingfor.sort_by(&:first).each do |e|
|
|
757
757
|
# Try to find an SMTP Status Code from the given string
|
|
758
|
-
indexofees = esmtperror.index(e[1], e[0]); next
|
|
758
|
+
indexofees = esmtperror.index(e[1], e[0]); next if indexofees.nil?
|
|
759
759
|
characters = [esmtperror[indexofees - 1, 1].ord] # [0] The previous character of the status
|
|
760
760
|
[2, 3].each do |i|
|
|
761
761
|
# [1] The value of the "Subject", "5.[7].261"
|
|
@@ -788,7 +788,7 @@ module Sisimai
|
|
|
788
788
|
next if characters[3] < 48 || characters[3] > 57 # The 1st digit of the detail is not a number
|
|
789
789
|
readbuffer << characters[3].chr
|
|
790
790
|
|
|
791
|
-
if
|
|
791
|
+
if Sisimai::SMTP::Status.is_ambiguous(readbuffer) || readbuffer == "4.4.7"
|
|
792
792
|
# Find another status code except *.0.0, 4.4.7
|
|
793
793
|
anotherone = readbuffer
|
|
794
794
|
next
|
|
@@ -827,8 +827,8 @@ module Sisimai
|
|
|
827
827
|
# @return [String] The preferred value
|
|
828
828
|
# @since v5.0.0
|
|
829
829
|
def prefer(argv0 = nil, argv1 = nil, argv2 = nil)
|
|
830
|
-
return argv1
|
|
831
|
-
return argv0
|
|
830
|
+
return argv1 if argv0.nil?; return argv1 if argv0.size < 5
|
|
831
|
+
return argv0 if argv1.nil?; return argv0 if argv1.size < 5
|
|
832
832
|
|
|
833
833
|
statuscode = argv0
|
|
834
834
|
codeinmesg = argv1
|
|
@@ -883,7 +883,7 @@ module Sisimai
|
|
|
883
883
|
return statuscode
|
|
884
884
|
end
|
|
885
885
|
|
|
886
|
-
# is_explicit() returns
|
|
886
|
+
# is_explicit() returns false when the argument is empty or is an internal code
|
|
887
887
|
# @param string argv1 Status code
|
|
888
888
|
# @return bool false: The delivery status code is not explicit
|
|
889
889
|
def is_explicit(argv1 = '')
|
|
@@ -891,6 +891,15 @@ module Sisimai
|
|
|
891
891
|
return false if argv1.size == 7 && argv1.start_with?("5.0.9", "4.0.9")
|
|
892
892
|
return true
|
|
893
893
|
end
|
|
894
|
+
|
|
895
|
+
# is_ambiguous() returns true when the argument is not empty and ends with ".0.0".
|
|
896
|
+
# @param string argv1 Status code
|
|
897
|
+
# @return bool false: The delivery status code is not ambiguous
|
|
898
|
+
def is_ambiguous(argv1 = '')
|
|
899
|
+
return true if argv1.nil? || argv1.empty?
|
|
900
|
+
return true if argv1.size == 5 && argv1.end_with?(".0.0")
|
|
901
|
+
return false
|
|
902
|
+
end
|
|
894
903
|
end
|
|
895
904
|
end
|
|
896
905
|
end
|
|
@@ -17,8 +17,8 @@ module Sisimai
|
|
|
17
17
|
return nil if argv0.size == 0
|
|
18
18
|
|
|
19
19
|
# 1. Replace label strings of SMTP client/server at the each line
|
|
20
|
-
argv0.gsub!(/^[ ]+#{argv1}\s+/m, '>>> '); return nil
|
|
21
|
-
argv0.gsub!(/^[ ]+#{argv2}\s+/m, '<<< '); return nil
|
|
20
|
+
argv0.gsub!(/^[ ]+#{argv1}\s+/m, '>>> '); return nil if argv0.include?('>>> ') == false
|
|
21
|
+
argv0.gsub!(/^[ ]+#{argv2}\s+/m, '<<< '); return nil if argv0.include?('<<< ') == false
|
|
22
22
|
|
|
23
23
|
# 2. Remove strings until the first '<<<' or '>>>'
|
|
24
24
|
esmtp = []
|
|
@@ -96,7 +96,7 @@ module Sisimai
|
|
|
96
96
|
end
|
|
97
97
|
else
|
|
98
98
|
# SMTP server sent a response "<<< response text"
|
|
99
|
-
p = e.index('<<< '); next
|
|
99
|
+
p = e.index('<<< '); next if p.nil? && p != 0
|
|
100
100
|
e = e[4, e.size].sub(/\A<<<[ ]/, '')
|
|
101
101
|
|
|
102
102
|
if cv = e.match(/\A([2-5]\d\d)[ ]/) then cx['response']['reply'] = cv[1] end
|
data/lib/sisimai/string.rb
CHANGED
|
@@ -14,10 +14,8 @@ module Sisimai
|
|
|
14
14
|
# @return [String] Message token(MD5 hex digest) or blank(failed to create token)
|
|
15
15
|
# @see http://en.wikipedia.org/wiki/ASCII
|
|
16
16
|
def token(addr1, addr2, epoch)
|
|
17
|
-
return ""
|
|
18
|
-
return ""
|
|
19
|
-
return "" unless epoch.is_a?(Integer)
|
|
20
|
-
return "" if addr1.empty? || addr2.empty?
|
|
17
|
+
return "" if addr1.is_a?(::String) == false || addr2.is_a?(::String) == false
|
|
18
|
+
return "" if addr1.empty? || addr2.empty? || epoch.is_a?(Integer) == false
|
|
21
19
|
|
|
22
20
|
# Format: STX(0x02) Sender-Address RS(0x1e) Recipient-Address ETX(0x03)
|
|
23
21
|
require 'digest/sha1'
|
|
@@ -30,7 +28,7 @@ module Sisimai
|
|
|
30
28
|
def is_8bit(argvs)
|
|
31
29
|
v = argvs.to_s
|
|
32
30
|
return false if v.empty?
|
|
33
|
-
return true
|
|
31
|
+
return true if v !~ /\A[\x00-\x7f]*\z/
|
|
34
32
|
return false
|
|
35
33
|
end
|
|
36
34
|
|
|
@@ -40,7 +38,7 @@ module Sisimai
|
|
|
40
38
|
# @example Clean up text
|
|
41
39
|
# sweep(' neko ') #=> 'neko'
|
|
42
40
|
def sweep(argv1)
|
|
43
|
-
return argv1
|
|
41
|
+
return argv1 if argv1.is_a?(::String) == false
|
|
44
42
|
argv1 = argv1.chomp.squeeze(' ').strip
|
|
45
43
|
argv1 = argv1.sub(/ [-]{2,}[^ ].+\z/, '')
|
|
46
44
|
return argv1
|
|
@@ -52,16 +50,14 @@ module Sisimai
|
|
|
52
50
|
# @return [Boolean]
|
|
53
51
|
# @since v5.0.0
|
|
54
52
|
def aligned(argv1, argv2)
|
|
55
|
-
return false if argv1.to_s.empty?
|
|
56
|
-
return false unless argv2.is_a? Array
|
|
57
|
-
return false unless argv2.size > 1
|
|
53
|
+
return false if argv1.to_s.empty? || argv2.is_a?(Array) == false || argv2.size < 2
|
|
58
54
|
|
|
59
55
|
align = -1
|
|
60
56
|
right = 0
|
|
61
57
|
argv2.each do |e|
|
|
62
58
|
# Get the position of each element in the 1st argument using index()
|
|
63
59
|
p = argv1.index(e, align + 1)
|
|
64
|
-
break
|
|
60
|
+
break if p == nil # Break this loop when there is no string in the 1st argument
|
|
65
61
|
align = e.length + p - 1 # There is an aligned string in the 1st argument
|
|
66
62
|
right += 1
|
|
67
63
|
end
|
data/lib/sisimai/version.rb
CHANGED