sisimai 5.4.1-java → 5.6.0-java

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.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codecovio.yml +1 -1
  3. data/.github/workflows/rake-test.yml +1 -1
  4. data/ChangeLog.md +56 -0
  5. data/LICENSE +1 -1
  6. data/Makefile +2 -4
  7. data/README-JA.md +29 -23
  8. data/README.md +30 -24
  9. data/lib/sisimai/address.rb +92 -44
  10. data/lib/sisimai/arf.rb +7 -8
  11. data/lib/sisimai/datetime.rb +2 -2
  12. data/lib/sisimai/fact/json.rb +1 -2
  13. data/lib/sisimai/fact/yaml.rb +1 -2
  14. data/lib/sisimai/fact.rb +76 -36
  15. data/lib/sisimai/lda.rb +2 -2
  16. data/lib/sisimai/lhost/activehunter.rb +4 -5
  17. data/lib/sisimai/lhost/amazonses.rb +4 -5
  18. data/lib/sisimai/lhost/apachejames.rb +2 -2
  19. data/lib/sisimai/lhost/biglobe.rb +6 -6
  20. data/lib/sisimai/lhost/courier.rb +7 -7
  21. data/lib/sisimai/lhost/domino.rb +6 -6
  22. data/lib/sisimai/lhost/dragonfly.rb +5 -5
  23. data/lib/sisimai/lhost/einsundeins.rb +5 -5
  24. data/lib/sisimai/lhost/exchange2003.rb +7 -7
  25. data/lib/sisimai/lhost/exchange2007.rb +5 -5
  26. data/lib/sisimai/lhost/exim.rb +13 -15
  27. data/lib/sisimai/lhost/ezweb.rb +3 -2
  28. data/lib/sisimai/lhost/fml.rb +9 -9
  29. data/lib/sisimai/lhost/gmail.rb +9 -9
  30. data/lib/sisimai/lhost/gmx.rb +3 -3
  31. data/lib/sisimai/lhost/googlegroups.rb +6 -7
  32. data/lib/sisimai/lhost/googleworkspace.rb +5 -6
  33. data/lib/sisimai/lhost/imailserver.rb +4 -4
  34. data/lib/sisimai/lhost/kddi.rb +4 -4
  35. data/lib/sisimai/lhost/mailfoundry.rb +3 -3
  36. data/lib/sisimai/lhost/{mailmarshalsmtp.rb → mailmarshal.rb} +5 -5
  37. data/lib/sisimai/lhost/messagingserver.rb +8 -8
  38. data/lib/sisimai/lhost/mfilter.rb +8 -4
  39. data/lib/sisimai/lhost/mimecast.rb +105 -0
  40. data/lib/sisimai/lhost/notes.rb +5 -5
  41. data/lib/sisimai/lhost/opensmtpd.rb +5 -5
  42. data/lib/sisimai/lhost/postfix.rb +38 -32
  43. data/lib/sisimai/lhost/qmail.rb +11 -11
  44. data/lib/sisimai/lhost/sendmail.rb +13 -13
  45. data/lib/sisimai/lhost/{interscanmss.rb → trendmicro.rb} +8 -9
  46. data/lib/sisimai/lhost/v5sendmail.rb +7 -7
  47. data/lib/sisimai/lhost/verizon.rb +3 -3
  48. data/lib/sisimai/lhost/x1.rb +7 -4
  49. data/lib/sisimai/lhost/x2.rb +40 -12
  50. data/lib/sisimai/lhost/x3.rb +3 -3
  51. data/lib/sisimai/lhost/x6.rb +2 -2
  52. data/lib/sisimai/lhost/zoho.rb +5 -5
  53. data/lib/sisimai/lhost.rb +18 -17
  54. data/lib/sisimai/mail/maildir.rb +4 -4
  55. data/lib/sisimai/mail/mbox.rb +4 -4
  56. data/lib/sisimai/mail/memory.rb +1 -1
  57. data/lib/sisimai/mail/stdin.rb +2 -2
  58. data/lib/sisimai/message.rb +36 -34
  59. data/lib/sisimai/order.rb +5 -4
  60. data/lib/sisimai/reason/authfailure.rb +10 -14
  61. data/lib/sisimai/reason/badreputation.rb +8 -8
  62. data/lib/sisimai/reason/blocked.rb +58 -83
  63. data/lib/sisimai/reason/contenterror.rb +15 -10
  64. data/lib/sisimai/reason/{mesgtoobig.rb → emailtoolarge.rb} +23 -26
  65. data/lib/sisimai/reason/expired.rb +17 -24
  66. data/lib/sisimai/reason/failedstarttls.rb +1 -1
  67. data/lib/sisimai/reason/filtered.rb +14 -18
  68. data/lib/sisimai/reason/hasmoved.rb +3 -2
  69. data/lib/sisimai/reason/hostunknown.rb +18 -21
  70. data/lib/sisimai/reason/mailboxfull.rb +28 -50
  71. data/lib/sisimai/reason/mailererror.rb +1 -1
  72. data/lib/sisimai/reason/networkerror.rb +17 -17
  73. data/lib/sisimai/reason/norelaying.rb +20 -20
  74. data/lib/sisimai/reason/notaccept.rb +7 -10
  75. data/lib/sisimai/reason/notcompliantrfc.rb +6 -10
  76. data/lib/sisimai/reason/policyviolation.rb +12 -28
  77. data/lib/sisimai/reason/ratelimited.rb +62 -0
  78. data/lib/sisimai/reason/rejected.rb +46 -58
  79. data/lib/sisimai/reason/requireptr.rb +14 -26
  80. data/lib/sisimai/reason/securityerror.rb +14 -20
  81. data/lib/sisimai/reason/spamdetected.rb +52 -102
  82. data/lib/sisimai/reason/suspend.rb +27 -24
  83. data/lib/sisimai/reason/systemerror.rb +20 -24
  84. data/lib/sisimai/reason/systemfull.rb +2 -2
  85. data/lib/sisimai/reason/userunknown.rb +82 -114
  86. data/lib/sisimai/reason/vacation.rb +1 -1
  87. data/lib/sisimai/reason/virusdetected.rb +7 -9
  88. data/lib/sisimai/reason.rb +26 -26
  89. data/lib/sisimai/rfc1123.rb +58 -18
  90. data/lib/sisimai/rfc1894.rb +6 -8
  91. data/lib/sisimai/rfc2045.rb +25 -13
  92. data/lib/sisimai/rfc3464/thirdparty.rb +2 -3
  93. data/lib/sisimai/rfc3464.rb +6 -6
  94. data/lib/sisimai/rfc3834.rb +18 -8
  95. data/lib/sisimai/rfc5322.rb +9 -9
  96. data/lib/sisimai/rfc791.rb +2 -2
  97. data/lib/sisimai/rhost/aol.rb +4 -1
  98. data/lib/sisimai/rhost/apple.rb +15 -11
  99. data/lib/sisimai/rhost/cloudflare.rb +2 -0
  100. data/lib/sisimai/rhost/cox.rb +31 -25
  101. data/lib/sisimai/rhost/facebook.rb +24 -18
  102. data/lib/sisimai/rhost/franceptt.rb +92 -38
  103. data/lib/sisimai/rhost/godaddy.rb +34 -7
  104. data/lib/sisimai/rhost/google.rb +69 -70
  105. data/lib/sisimai/rhost/gsuite.rb +1 -1
  106. data/lib/sisimai/rhost/iua.rb +1 -1
  107. data/lib/sisimai/rhost/kddi.rb +1 -1
  108. data/lib/sisimai/rhost/messagelabs.rb +160 -2
  109. data/lib/sisimai/rhost/microsoft.rb +154 -107
  110. data/lib/sisimai/rhost/mimecast.rb +64 -55
  111. data/lib/sisimai/rhost/nttdocomo.rb +70 -90
  112. data/lib/sisimai/rhost/outlook.rb +1 -1
  113. data/lib/sisimai/rhost/spectrum.rb +8 -8
  114. data/lib/sisimai/rhost/tencent.rb +12 -13
  115. data/lib/sisimai/rhost/yahooinc.rb +9 -10
  116. data/lib/sisimai/rhost/zoho.rb +72 -0
  117. data/lib/sisimai/rhost.rb +4 -3
  118. data/lib/sisimai/smtp/command.rb +4 -2
  119. data/lib/sisimai/smtp/reply.rb +11 -4
  120. data/lib/sisimai/smtp/status.rb +67 -98
  121. data/lib/sisimai/smtp/transcript.rb +3 -3
  122. data/lib/sisimai/string.rb +4 -23
  123. data/lib/sisimai/version.rb +1 -1
  124. data/lib/sisimai.rb +1 -1
  125. data/set-of-emails/maildir/bsd/lhost-exim-56.eml +40 -40
  126. data/set-of-emails/maildir/bsd/lhost-mfilter-05.eml +41 -0
  127. data/set-of-emails/maildir/bsd/lhost-mimecast-01.eml +46 -0
  128. data/set-of-emails/maildir/bsd/lhost-mimecast-02.eml +59 -0
  129. data/set-of-emails/maildir/bsd/lhost-postfix-79.eml +81 -0
  130. data/set-of-emails/maildir/bsd/lhost-postfix-80.eml +84 -0
  131. data/set-of-emails/maildir/bsd/lhost-x1-03.eml +26 -0
  132. data/set-of-emails/maildir/bsd/lhost-x1-04.eml +45 -0
  133. data/set-of-emails/maildir/bsd/lhost-x2-07.eml +30 -0
  134. data/set-of-emails/maildir/bsd/rfc3464-66.eml +170 -0
  135. data/set-of-emails/maildir/bsd/rfc3834-06.eml +59 -0
  136. data/set-of-emails/maildir/bsd/rhost-zoho-01.eml +88 -0
  137. data/set-of-emails/maildir/bsd/rhost-zoho-02.eml +86 -0
  138. data/set-of-emails/maildir/bsd/rhost-zoho-03.eml +87 -0
  139. data/set-of-emails/maildir/bsd/rhost-zoho-04.eml +86 -0
  140. data/set-of-emails/maildir/not/rb-issue-368-bug.eml +39 -0
  141. data/sisimai-java.gemspec +1 -1
  142. data/sisimai.gemspec +1 -1
  143. metadata +28 -13
  144. data/lib/sisimai/reason/exceedlimit.rb +0 -47
  145. data/lib/sisimai/reason/speeding.rb +0 -47
  146. data/lib/sisimai/reason/toomanyconn.rb +0 -59
  147. /data/set-of-emails/maildir/bsd/{lhost-mailmarshalsmtp-02.eml → lhost-mailmarshal-02.eml} +0 -0
  148. /data/set-of-emails/maildir/bsd/{lhost-interscanmss-01.eml → lhost-trendmicro-01.eml} +0 -0
  149. /data/set-of-emails/maildir/bsd/{lhost-interscanmss-02.eml → lhost-trendmicro-02.eml} +0 -0
  150. /data/set-of-emails/maildir/bsd/{lhost-interscanmss-03.eml → lhost-trendmicro-03.eml} +0 -0
@@ -7,8 +7,8 @@ module Sisimai
7
7
  class << self
8
8
  MessagesOf = {
9
9
  'mailboxfull' => ['552 too much mail data'],
10
+ 'ratelimited' => ['552 too many recipients'],
10
11
  'syntaxerror' => ['503 bad sequence of commands', '504 command parameter not implemented'],
11
- 'toomanyconn' => ['552 too many recipients'],
12
12
  'userunknown' => ['550 unknown user'],
13
13
  }.freeze
14
14
 
@@ -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'] unless argvs['reason'].empty?
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
- if statuscode == '5.1.1' || statuscode == '5.0.911'
27
- # ----- Transcript of session follows -----
28
- # ... while talking to mfsmax.docomo.ne.jp.:
29
- # >>> RCPT To:<***@docomo.ne.jp>
30
- # <<< 550 Unknown user ***@docomo.ne.jp
31
- # 550 5.1.1 <***@docomo.ne.jp>... User unknown
32
- # >>> DATA
33
- # <<< 503 Bad sequence of commands
34
- reasontext = 'userunknown'
35
-
36
- elsif statuscode == '5.2.0'
37
- # ----- The following addresses had permanent fatal errors -----
38
- # <***@docomo.ne.jp>
39
- # (reason: 550 Unknown user ***@docomo.ne.jp)
40
- #
41
- # ----- Transcript of session follows -----
42
- # ... while talking to mfsmax.docomo.ne.jp.:
43
- # >>> DATA
44
- # <<< 550 Unknown user ***@docomo.ne.jp
45
- # 554 5.0.0 Service unavailable
46
- # ...
47
- # Final-Recipient: RFC822; ***@docomo.ne.jp
48
- # Action: failed
49
- # Status: 5.2.0
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.9.213'
48
+ return 'filtered' if statuscode == '5.2.0'
51
49
 
52
- else
50
+ MessagesOf.each_key do |e|
53
51
  # The value of "Diagnostic-Code:" field is not empty
54
- MessagesOf.each_key do |e|
55
- # - The key name is a bounce reason name
56
- # - https://github.com/sisimai/go-sisimai/issues/64
57
- # - After March 12, 2025, if an error message contains "550 Unknown user", the
58
- # bounce reason will be definitively "userunknown". This is because NTT DOCOMO
59
- # no longer rejects emails via SMTP for domain-specific rejection or specified
60
- # reception filters.
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
- if reasontext.empty?
68
- # A bounce reason did not decide from a status code, an error message.
69
- if statuscode == '5.0.0'
70
- # Status: 5.0.0
71
- if thecommand == 'RCPT'
72
- # Your message to the following recipients cannot be delivered:
73
- #
74
- # <***@docomo.ne.jp>:
75
- # mfsmax.docomo.ne.jp [203.138.181.112]:
76
- # >>> RCPT TO:<***@docomo.ne.jp>
77
- # <<< 550 Unknown user ***@docomo.ne.jp
78
- # ...
79
- #
80
- # Final-Recipient: rfc822; ***@docomo.ne.jp
81
- # Action: failed
82
- # Status: 5.0.0
83
- # Remote-MTA: dns; mfsmax.docomo.ne.jp [203.138.181.112]
84
- # Diagnostic-Code: smtp; 550 Unknown user ***@docomo.ne.jp
85
- reasontext = 'userunknown'
86
-
87
- elsif thecommand == 'DATA'
88
- # <***@docomo.ne.jp>: host mfsmax.docomo.ne.jp[203.138.181.240] said:
89
- # 550 Unknown user ***@docomo.ne.jp (in reply to end of DATA
90
- # command)
91
- # ...
92
- # Final-Recipient: rfc822; ***@docomo.ne.jp
93
- # Original-Recipient: rfc822;***@docomo.ne.jp
94
- # Action: failed
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
- return reasontext
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 unless MessagesOf[e].any? { |a| issuedcode.include?(a) }
24
+ next if MessagesOf[e].none? { |a| issuedcode.include?(a) }
25
25
  reasontext = e
26
26
  break
27
27
  end
@@ -62,11 +62,11 @@ module Sisimai
62
62
  # 1300 Spectrum limits the number of concurrent connections from a sender, as well as the
63
63
  # 1340 total number of connections allowed. Limits vary based on the reputation of the IP
64
64
  # address. Reduce your number of connections and try again later.
65
- [1300, 1340, 'toomanyconn'],
65
+ [1300, 1340, 'ratelimited'],
66
66
 
67
67
  # 1350 Spectrum limits emails by the number of messages sent, amount of recipients,
68
68
  # 1490 potential for spam and invalid recipients.
69
- [1350, 1490, 'speeding'],
69
+ [1350, 1490, 'ratelimited'],
70
70
 
71
71
  # 1500 Your email was rejected for attempting to send as a different email address than you
72
72
  # signed in under. Check that you're sending emails from the address you signed in with.
@@ -77,23 +77,23 @@ module Sisimai
77
77
  [1520, 0, 'rejected'],
78
78
 
79
79
  # 1530 Your email was rejected because it's larger than the maximum size of 20MB.
80
- [1530, 0, 'mesgtoobig'],
80
+ [1530, 0, 'emailtoolarge'],
81
81
 
82
82
  # 1540 Your emails were deferred for attempting to send too many in a single session.
83
83
  # Reconnect and try reducing the number of emails you send at one time.
84
- [1540, 0, 'speeding'],
84
+ [1540, 0, 'ratelimited'],
85
85
 
86
86
  # 1550 Your email was rejected for having too many recipients in one message. Reduce the
87
87
  # number of recipients and try again later.
88
- [1550, 0, 'speeding'],
88
+ [1550, 0, 'ratelimited'],
89
89
 
90
90
  # 1560 Your email was rejected for having too many invalid recipients. Check your outgoing
91
91
  # email addresses and try again later.
92
- [1560, 0, 'policyviolation'],
92
+ [1560, 0, 'ratelimited'],
93
93
 
94
94
  # 1580 You've tried to send messages to too many recipients in a short period of time.
95
95
  # Wait a little while and try again later.
96
- [1580, 0, 'speeding'],
96
+ [1580, 0, 'ratelimited'],
97
97
  ].freeze
98
98
 
99
99
  # Detect bounce reason from https://www.spectrum.com/
@@ -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'] unless argvs['reason'].empty?
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
@@ -15,32 +15,31 @@ module Sisimai
15
15
  'suspected spam ip', # https://service.mail.qq.com/detail/122/66
16
16
  'connection denied', # https://service.mail.qq.com/detail/122/170
17
17
  ],
18
- 'mesgtoobig' => [
18
+ 'emailtoolarge' => [
19
19
  'message too large', # https://service.mail.qq.com/detail/122/168
20
20
  ],
21
+ 'ratelimited' => [
22
+ 'mailbox unavailable or access denined', # https://service.mail.qq.com/detail/122/166
23
+ 'ip frequency limited', # https://service.mail.qq.com/detail/122/172
24
+ 'domain frequency limited', # https://service.mail.qq.com/detail/122/173
25
+ 'sender frequency limited', # https://service.mail.qq.com/detail/122/174
26
+ 'connection frequency limited', # https://service.mail.qq.com/detail/122/175
27
+ "frequency of receiving messages is limited", # https://service.mail.qq.com/detail/122/1011
28
+ ],
21
29
  'rejected' => [
22
30
  'suspected spam', # https://service.mail.qq.com/detail/122/71
23
31
  'mail is rejected by recipients', # https://service.mail.qq.com/detail/122/92
24
32
  ],
25
- 'spandetected' => [
33
+ 'spamdetected' => [
26
34
  'spam is embedded in the email', # https://service.mail.qq.com/detail/122/59
27
35
  'mail content denied', # https://service.mail.qq.com/detail/122/171
28
36
  ],
29
- 'speeding' => [
30
- 'mailbox unavailable or access denined', # https://service.mail.qq.com/detail/122/166
31
- ],
32
37
  'suspend' => [
33
38
  'is a deactivated mailbox', # http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=20022&&no=1000742
34
39
  ],
35
40
  'syntaxerror' => [
36
41
  'bad address syntax', # https://service.mail.qq.com/detail/122/167
37
42
  ],
38
- 'toomanyconn' => [
39
- 'ip frequency limited', # https://service.mail.qq.com/detail/122/172
40
- 'domain frequency limited', # https://service.mail.qq.com/detail/122/173
41
- 'sender frequency limited', # https://service.mail.qq.com/detail/122/174
42
- 'connection frequency limited', # https://service.mail.qq.com/detail/122/175
43
- ],
44
43
  'userunknown' => [
45
44
  'mailbox not found', # https://service.mail.qq.com/detail/122/169
46
45
  ],
@@ -50,13 +49,13 @@ module Sisimai
50
49
  # @param [Sisimai::Fact] argvs Decoded email object
51
50
  # @return [String] The bounce reason at Tencent QQ
52
51
  def find(argvs)
53
- return argvs['reason'] unless argvs['reason'].empty?
52
+ return argvs['reason'] if argvs['reason'].empty? == false
54
53
  issuedcode = argvs['diagnosticcode'].downcase
55
54
  reasontext = ''
56
55
 
57
56
  MessagesOf.each_key do |e|
58
57
  MessagesOf[e].each do |f|
59
- next unless issuedcode.include?(f)
58
+ next if issuedcode.include?(f) == false
60
59
  reasontext = e
61
60
  break
62
61
  end
@@ -31,6 +31,13 @@ module Sisimai
31
31
  # See https://postmaster.yahooinc.com/error-codes
32
32
  'not accepted for policy reasons',
33
33
  ],
34
+ 'ratelimited' => [
35
+ # - 421 Max message per connection reached, closing transmission channel
36
+ 'max message per connection reached',
37
+
38
+ # - 450 User is receiving mail too quickly
39
+ 'user is receiving mail too quickly',
40
+ ],
34
41
  'rejected' => [
35
42
  # Observed the following error message since around March 2024:
36
43
  #
@@ -53,10 +60,6 @@ module Sisimai
53
60
  # Retrying will NOT succeed. See https://postmaster.yahooinc.com/error-codes
54
61
  ' will be permanently deferred',
55
62
  ],
56
- 'speeding' => [
57
- # - 450 User is receiving mail too quickly
58
- 'user is receiving mail too quickly',
59
- ],
60
63
  'suspend' => [
61
64
  # - 554 delivery error: dd ****@yahoo.com is no longer valid.
62
65
  # - 554 30 Sorry, your message to *****@aol.jp cannot be delivered.
@@ -68,10 +71,6 @@ module Sisimai
68
71
  # - 501 Syntax error in parameters or arguments
69
72
  'syntax error in parameters or arguments',
70
73
  ],
71
- 'toomanyconn' => [
72
- # - 421 Max message per connection reached, closing transmission channel
73
- 'max message per connection reached',
74
- ],
75
74
  'userunknown' => [
76
75
  # - 554 delivery error: dd This user doesn't have a yahoo.com account (***@yahoo.com)
77
76
  # - 552 1 Requested mail action aborted, mailbox not found (in reply to end of DATA command)
@@ -88,13 +87,13 @@ module Sisimai
88
87
  # https://www.postmastery.com/yahoo-postmaster/
89
88
  # @since v5.1.0
90
89
  def find(argvs)
91
- return argvs['reason'] unless argvs['reason'].empty?
90
+ return argvs['reason'] if argvs['reason'].empty? == false
92
91
  issuedcode = argvs['diagnosticcode'].downcase
93
92
  reasontext = ''
94
93
 
95
94
  MessagesOf.each_key do |e|
96
95
  MessagesOf[e].each do |f|
97
- next unless issuedcode.include?(f)
96
+ next if issuedcode.include?(f) == false
98
97
  reasontext = e
99
98
  break
100
99
  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 unless RhostClass[e].any? { |a| a.end_with?(domainpart) }
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 unless RhostClass[e].any? { |a| remotehost.end_with?(a) }
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 unless RhostClass[e].any? { |a| clienthost.end_with?(a) }
63
+ next if RhostClass[e].none? { |a| clienthost.end_with?(a) }
63
64
  rhostclass = e
64
65
  throw :FINDRHOST
65
66
  end
@@ -2,6 +2,8 @@ module Sisimai
2
2
  module SMTP
3
3
  # Sisimai::SMTP::Transcript is an SMTP Command related utilities
4
4
  module Command
5
+ ExceptDATA = ["CONN", "EHLO", "HELO", "MAIL", "RCPT"].freeze
6
+ BeforeRCPT = ["CONN", "EHLO", "EHLO", "MAIL", "AUTH", "STARTTLS"].freeze
5
7
  class << self
6
8
  Availables = [
7
9
  "HELO", "EHLO", "MAIL", "RCPT", "DATA", "QUIT", "RSET", "NOOP", "VRFY", "ETRN", "EXPN",
@@ -28,7 +30,7 @@ module Sisimai
28
30
  # @return [String] An SMTP command
29
31
  # @since v5.0.0
30
32
  def find(argv0 = '')
31
- return "" unless Sisimai::SMTP::Command.test(argv0)
33
+ return "" if Sisimai::SMTP::Command.test(argv0) == false
32
34
 
33
35
  issuedcode = " " + argv0.downcase + " "
34
36
  commandmap = {"STAR" => "STARTTLS", "XFOR" => "XFORWARD"}
@@ -36,7 +38,7 @@ module Sisimai
36
38
 
37
39
  Detectable.each do |e|
38
40
  # Find an SMTP command from the given string
39
- p0 = argv0.index(e); next unless p0
41
+ p0 = argv0.index(e); next if p0.nil?
40
42
  if e.include?(" ") == false
41
43
  # For example, "RCPT T" does not appear in an email address or a domain name
42
44
  cx = true; while true do
@@ -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", "", "securityerror"], # RFC5248
123
- "524" => ["AUTH", "", "securityerror"], # RFC5248
124
- "525" => ["AUTH", "", "securityerror"], # RFC5248
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 unless reply == 334 || reply == 354
163
+ return false if reply != 334 && reply != 354
157
164
  return true
158
165
  end
159
166