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
@@ -20,7 +20,7 @@ module Sisimai::Lhost
20
20
  match = 0
21
21
  match += 1 if mhead['content-type'].include?('Boundary_(ID_')
22
22
  match += 1 if mhead['subject'].start_with?('Delivery Notification: ')
23
- return nil unless match > 0
23
+ return nil if match == 0
24
24
 
25
25
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
26
26
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
@@ -63,7 +63,7 @@ module Sisimai::Lhost
63
63
  # Recipient address: @smtp.example.net:kijitora@server
64
64
  # Original address: kijitora@example.jp
65
65
  cv = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
66
- next unless Sisimai::Address.is_emailaddress(cv)
66
+ next if Sisimai::Address.is_emailaddress(cv) == false
67
67
 
68
68
  if v["recipient"] != "" && cv != v['recipient']
69
69
  # There are multiple recipient addresses in the message body.
@@ -99,9 +99,9 @@ module Sisimai::Lhost
99
99
 
100
100
  # The value does not include ".", use IP address instead.
101
101
  # (TCP|17.111.174.67|47323|192.0.2.225|25)
102
- next unless sessionlog[0] == '(TCP'
102
+ next if sessionlog[0] != '(TCP'
103
103
  v['lhost'] = sessionlog[1]
104
- v['rhost'] = sessionlog[3] unless remotehost.index('.') > 1
104
+ v['rhost'] = sessionlog[3] if remotehost.index('.') < 2
105
105
  else
106
106
  # Original-envelope-id: 0NFC009FLKOUVMA0@mr21p30im-asmtp004.me.com
107
107
  # Reporting-MTA: dns;mr21p30im-asmtp004.me.com (tcp-daemon)
@@ -118,7 +118,7 @@ module Sisimai::Lhost
118
118
  if e.start_with?('Status: ')
119
119
  # Status: 5.1.1 (Remote SMTP server has rejected address)
120
120
  p1 = e.index(':')
121
- p2 = e.index('(')
121
+ p2 = e.index('('); next if p2.nil?
122
122
  v['status'] = e[p1 + 2, p2 - p1 - 3]
123
123
  v['diagnosis'] = e[p2 + 1, e[e.index(')') - p2 - 1]] if v["diagnosis"].empty?
124
124
 
@@ -130,17 +130,17 @@ module Sisimai::Lhost
130
130
  # Reporting-MTA: dns;mr21p30im-asmtp004.me.com (tcp-daemon)
131
131
  localhost = e[e.index(';') + 1, e.size]
132
132
  v['lhost'] = localhost if v["lhost"].empty?
133
- v['lhost'] = localhost unless v['lhost'].index('.') > 0
133
+ v['lhost'] = localhost if v['lhost'].index('.') < 1
134
134
  end
135
135
  end
136
136
  end
137
- return nil unless recipients > 0
137
+ return nil if recipients == 0
138
138
 
139
139
  dscontents.each do |e|
140
140
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
141
141
  MessagesOf.each_key do |r|
142
142
  # Verify each regular expression of session errors
143
- next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
143
+ next if MessagesOf[r].none? { |a| e['diagnosis'].include?(a) }
144
144
  e['reason'] = r
145
145
  break
146
146
  end
@@ -19,8 +19,12 @@ module Sisimai::Lhost
19
19
  # @return [Nil] it failed to decode or the arguments are missing
20
20
  def inquire(mhead, mbody)
21
21
  # X-Mailer: m-FILTER
22
- return nil unless mhead['x-mailer'].to_s == 'm-FILTER'
23
- return nil unless mhead['subject'] == 'failure notice'
22
+ proceedsto = false
23
+ proceedsto = true if mhead['x-mailer'].to_s == 'm-FILTER'
24
+ proceedsto = true if Boundaries.any? { |a| mbody.include?(a) }
25
+ proceedsto = true if StartingOf[:error].any? { |a| mbody.include?(a) }
26
+ proceedsto = true if StartingOf[:command].any? { |a| mbody.include?(a) }
27
+ return nil if proceedsto == false
24
28
 
25
29
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
26
30
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
@@ -91,7 +95,7 @@ module Sisimai::Lhost
91
95
  end
92
96
  end
93
97
  end
94
- return nil unless recipients > 0
98
+ return nil if recipients == 0
95
99
 
96
100
  dscontents.each do |e|
97
101
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
@@ -105,7 +109,7 @@ module Sisimai::Lhost
105
109
  e['lhost'] = Sisimai::RFC5322.received(rheads[0]).shift if e["lhost"].empty?
106
110
  [rhosts[0], rhosts[1]].each do |ee|
107
111
  # Avoid "... by m-FILTER"
108
- next unless ee.include?('.')
112
+ next if ee.include?('.') == false
109
113
  e['rhost'] = ee
110
114
  end
111
115
  end
@@ -0,0 +1,105 @@
1
+ module Sisimai::Lhost
2
+ # Sisimai::Lhost::Mimecast decodes a bounce email which created by Mimecast https://www.mimecast.com/.
3
+ # Methods in the module are called from only Sisimai::Message.
4
+ module Mimecast
5
+ class << self
6
+ require 'sisimai/lhost'
7
+
8
+ Indicators = Sisimai::Lhost.INDICATORS
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze # No such line in lhost-mimecast-*
10
+ StartingOf = {message: ['-- ']}.freeze
11
+
12
+ # @abstract Decodes the bounce message from Mimecast
13
+ # @param [Hash] mhead Message headers of a bounce email
14
+ # @param [String] mbody Message body of a bounce email
15
+ # @return [Hash] Bounce data list and message/rfc822 part
16
+ # @return [Nil] it failed to decode or the arguments are missing
17
+ def inquire(mhead, mbody)
18
+ match = 0
19
+ match += 1 if mhead['subject'].include?('Email Delivery Failure')
20
+ if mhead['message-id']
21
+ # Message-Id: <0123456789-1102117314000@us-mta-25.us.mimecast.lan>
22
+ match += 1 if mhead['message-id'].include?('.mimecast.lan>')
23
+ end
24
+ return nil if match == 0
25
+
26
+ require 'sisimai/rfc1123'
27
+ require 'sisimai/rfc1894'
28
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
29
+ permessage = {} # (Hash) Store values of each Per-Message field
30
+
31
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
32
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
33
+ bodyslices = emailparts[0].split("\n")
34
+ readcursor = 0 # (Integer) Points the current cursor position
35
+ recipients = 0 # (Integer) The number of 'Final-Recipient' header
36
+
37
+ while e = bodyslices.shift do
38
+ # Read error messages and delivery status lines from the head of the email to the previous
39
+ # line of the beginning of the original message.
40
+ if readcursor == 0
41
+ # Beginning of the bounce message or message/delivery-status part
42
+ readcursor |= Indicators[:deliverystatus] if e.include?(StartingOf[:message][0])
43
+ end
44
+ next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty?
45
+
46
+ v = dscontents[-1]
47
+ if e.start_with?('-- ')
48
+ # An email that you attempted to send to the following address could not be delivered:
49
+ # -- sabineko@neko.ef.example.org
50
+ cv = e[3, 256]
51
+ if cv.include?(' ') == false
52
+ # -- sotoneko@cat.example.com
53
+ if v["recipient"] != ""
54
+ # There are multiple recipient addresses in the message body.
55
+ dscontents << Sisimai::Lhost.DELIVERYSTATUS
56
+ v = dscontents[-1]
57
+ end
58
+ v['recipient'] = Sisimai::Address.s3s4(cv)
59
+ recipients += 1
60
+ else
61
+ # Deal each line begins with "-- " as an error message.
62
+ # The problem appears to be :
63
+ # -- Recipient email address is possibly incorrect
64
+ # Additional information follows :
65
+ # -- 5.4.1 Recipient address rejected: Access denied.
66
+ v["diagnosis"] += cv + " "
67
+ end
68
+ else
69
+ # Lines after Content-Type: message/delivery-status
70
+ f = Sisimai::RFC1894.match(e)
71
+ if f > 0
72
+ # "e" matched with any field defined in RFC3464
73
+ next unless o = Sisimai::RFC1894.field(e)
74
+
75
+ if o[3] == 'code'
76
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
77
+ v['spec'] = o[1]
78
+ v['diagnosis'] = o[2]
79
+ else
80
+ # Other DSN fields defined in RFC3464
81
+ next if fieldtable[o[0]].nil?
82
+ next if o[3] == "host" && Sisimai::RFC1123.is_internethost(o[2]) == false
83
+ v[fieldtable[o[0]]] = o[2]
84
+
85
+ next if f != 1
86
+ permessage[fieldtable[o[0]]] = o[2]
87
+ end
88
+ end
89
+ end
90
+ end
91
+ return nil if recipients == 0
92
+
93
+ dscontents.each do |e|
94
+ # Set default values if each value is empty.
95
+ permessage.each_key { |a| e[a] ||= permessage[a] || '' }
96
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
97
+ end
98
+
99
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
100
+ end
101
+ def description; return 'Mimecast'; end
102
+ end
103
+ end
104
+ end
105
+
@@ -22,7 +22,7 @@ module Sisimai::Lhost
22
22
  # @return [Hash] Bounce data list and message/rfc822 part
23
23
  # @return [Nil] it failed to decode or the arguments are missing
24
24
  def inquire(mhead, mbody)
25
- return nil unless mhead['subject'].start_with?('Undeliverable message')
25
+ return nil if mhead['subject'].start_with?('Undeliverable message') == false
26
26
 
27
27
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
28
28
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
@@ -89,16 +89,16 @@ module Sisimai::Lhost
89
89
  end
90
90
  end
91
91
 
92
- unless recipients > 0
92
+ if recipients == 0
93
93
  # Fallback: Get the recpient address from RFC822 part
94
94
  p1 = emailparts[1].index("\nTo: ") || -1
95
95
  p2 = emailparts[1].index("\n", p1 + 6) || -1
96
96
  if p1 > 0
97
97
  v['recipient'] = Sisimai::Address.s3s4(emailparts[1][p1 + 5, p2 - p1 - 5])
98
- recipients += 1 unless v['recipient'].empty?
98
+ recipients += 1 if v['recipient'].empty? == false
99
99
  end
100
100
  end
101
- return nil unless recipients > 0
101
+ return nil if recipients == 0
102
102
 
103
103
  dscontents.each do |e|
104
104
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
@@ -106,7 +106,7 @@ module Sisimai::Lhost
106
106
 
107
107
  MessagesOf.each_key do |r|
108
108
  # Check each regular expression of Notes error messages
109
- next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
109
+ next if MessagesOf[r].none? { |a| e['diagnosis'].include?(a) }
110
110
  e['reason'] = r
111
111
  e['status'] = Sisimai::SMTP::Status.code(r.to_s) || ''
112
112
  break
@@ -72,9 +72,9 @@ module Sisimai::Lhost
72
72
  # @return [Hash] Bounce data list and message/rfc822 part
73
73
  # @return [Nil] it failed to decode or the arguments are missing
74
74
  def inquire(mhead, mbody)
75
- return nil unless mhead['subject'].start_with?('Delivery status notification')
76
- return nil unless mhead['from'].start_with?('Mailer Daemon <')
77
- return nil unless mhead['received'].any? { |a| a.include?(' (OpenSMTPD) with ') }
75
+ return nil if mhead['subject'].start_with?('Delivery status notification') == false
76
+ return nil if mhead['from'].start_with?('Mailer Daemon <') == false
77
+ return nil if mhead['received'].none? { |a| a.include?(' (OpenSMTPD) with ') }
78
78
 
79
79
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
80
80
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
@@ -116,13 +116,13 @@ module Sisimai::Lhost
116
116
  recipients += 1
117
117
  end
118
118
  end
119
- return nil unless recipients > 0
119
+ return nil if recipients == 0
120
120
 
121
121
  dscontents.each do |e|
122
122
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
123
123
  MessagesOf.each_key do |r|
124
124
  # Verify each regular expression of session errors
125
- next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
125
+ next if MessagesOf[r].none? { |a| e['diagnosis'].include?(a) }
126
126
  e['reason'] = r
127
127
  break
128
128
  end
@@ -6,6 +6,7 @@ module Sisimai::Lhost
6
6
  require 'sisimai/lhost'
7
7
  require 'sisimai/rfc1123'
8
8
  require 'sisimai/smtp/reply'
9
+ require 'sisimai/smtp/status'
9
10
  require 'sisimai/smtp/command'
10
11
 
11
12
  # Postfix manual - bounce(5) - http://www.postfix.org/bounce.5.html
@@ -55,8 +56,7 @@ module Sisimai::Lhost
55
56
  require 'sisimai/smtp/transcript'
56
57
  transcript = Sisimai::SMTP::Transcript.rise(emailparts[0], 'In:', 'Out:')
57
58
 
58
- return nil unless transcript
59
- return nil if transcript.size == 0
59
+ return nil if transcript.nil? || transcript.size == 0
60
60
 
61
61
  transcript.each do |e|
62
62
  # Pick email addresses, error messages, and the last SMTP command.
@@ -113,18 +113,22 @@ module Sisimai::Lhost
113
113
  if o[3] == 'addr'
114
114
  # Final-Recipient: rfc822; kijitora@example.jp
115
115
  # X-Actual-Recipient: rfc822; kijitora@example.co.jp
116
- if o[0] == 'final-recipient'
117
- # Final-Recipient: rfc822; kijitora@example.jp
118
- if v["recipient"] != ""
119
- # There are multiple recipient addresses in the message body.
120
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
121
- v = dscontents[-1]
116
+ if Sisimai::Address.is_emailaddress(o[2])
117
+ # The email address is a valid email address, avoid an email address
118
+ # without a valid domain part such as "neko@mailhost".
119
+ if o[0] == 'final-recipient'
120
+ # Final-Recipient: rfc822; kijitora@example.jp
121
+ if v["recipient"] != ""
122
+ # There are multiple recipient addresses in the message body.
123
+ dscontents << Sisimai::Lhost.DELIVERYSTATUS
124
+ v = dscontents[-1]
125
+ end
126
+ v['recipient'] = o[2]
127
+ recipients += 1
128
+ else
129
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
130
+ v['alias'] = o[2]
122
131
  end
123
- v['recipient'] = o[2]
124
- recipients += 1
125
- else
126
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
127
- v['alias'] = o[2]
128
132
  end
129
133
  elsif o[3] == 'code'
130
134
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
@@ -133,11 +137,11 @@ module Sisimai::Lhost
133
137
  v['diagnosis'] = o[2]
134
138
  else
135
139
  # Other DSN fields defined in RFC3464
136
- next unless fieldtable[o[0]]
140
+ next if fieldtable[o[0]].nil?
137
141
  next if o[3] == "host" && Sisimai::RFC1123.is_internethost(o[2]) == false
138
142
  v[fieldtable[o[0]]] = o[2]
139
143
 
140
- next unless f == 1
144
+ next if f != 1
141
145
  permessage[fieldtable[o[0]]] = o[2]
142
146
  end
143
147
  else
@@ -161,15 +165,15 @@ module Sisimai::Lhost
161
165
  # Alternative error message and recipient
162
166
  if e.include?(' (in reply to ') || e.include?('command)')
163
167
  # 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
164
- cv = Sisimai::SMTP::Command.find(e) || ""; commandset << cv unless cv.empty?
168
+ cv = Sisimai::SMTP::Command.find(e) || ""; commandset << cv if cv.empty? == false
165
169
  anotherset['diagnosis'] ||= ''
166
170
  anotherset['diagnosis'] += " #{e}"
167
171
 
168
- elsif Sisimai::String.aligned(e, ['<', '@', '>', '(expanded from<', '):'])
172
+ elsif Sisimai::String.aligned(e, ['<', '@', '>', '(expanded from ', '):'])
169
173
  # <r@example.ne.jp> (expanded from <kijitora@example.org>): user ...
170
- p1 = e.index('> ')
171
- p2 = e.index('(expanded from ', p1)
172
- p3 = e.index('>): ', p2 + 14)
174
+ p1 = e.index('> '); next unless p1
175
+ p2 = e.index('(expanded from ', p1); next unless p2
176
+ p3 = e.index('>): ', p2 + 14); next unless p3
173
177
  anotherset['recipient'] = Sisimai::Address.s3s4(e[0, p1])
174
178
  anotherset['alias'] = Sisimai::Address.s3s4(e[p2 + 15, p3 - p2 - 15])
175
179
  anotherset['diagnosis'] = e[p3 + 3, e.size]
@@ -192,7 +196,7 @@ module Sisimai::Lhost
192
196
  nomessages = true
193
197
  else
194
198
  # Get an error message continued from the previous line
195
- next unless anotherset['diagnosis']
199
+ next if anotherset['diagnosis'].nil?
196
200
  if e.start_with?(' ')
197
201
  # host mx.example.jp said:...
198
202
  anotherset['diagnosis'] += " #{e[4, e.size]}"
@@ -201,16 +205,20 @@ module Sisimai::Lhost
201
205
  end
202
206
  end
203
207
  end # end of while()
204
-
205
208
  end
206
209
 
207
210
  if recipients == 0
208
211
  # Fallback: get a recipient address from error messages
209
- if anotherset['recipient'].to_s.size > 0
210
- # Set a recipient address
211
- dscontents[-1]['recipient'] = anotherset['recipient']
212
+ %w[recipient alias].each do |e|
213
+ # Set a valid recipient address picked from the anotherset
214
+ next if anotherset[e].nil? || Sisimai::Address.is_emailaddress(anotherset[e]) == false
215
+ break if dscontents[-1]['recipient'].empty? == false
216
+ dscontents[-1]['recipient'] = anotherset[e]
212
217
  recipients += 1
213
- else
218
+ break
219
+ end
220
+
221
+ if recipients == 0
214
222
  # Get a recipient address from message/rfc822 part if the delivery report was unavailable:
215
223
  # '--- Delivery report unavailable ---'
216
224
  p1 = emailparts[1].index("\nTo: ") || -1
@@ -222,7 +230,7 @@ module Sisimai::Lhost
222
230
  end
223
231
  end
224
232
  end
225
- return nil unless recipients > 0
233
+ return nil if recipients == 0
226
234
 
227
235
  dscontents.each do |e|
228
236
  # Set default values if each value is empty.
@@ -240,13 +248,11 @@ module Sisimai::Lhost
240
248
  # More detailed error message is in "anotherset"
241
249
  as = '' # status
242
250
  ar = '' # replycode
243
- if e['status'].empty? || e['status'].start_with?('4.0.0', '5.0.0')
251
+ if Sisimai::SMTP::Status.is_ambiguous(e['status'])
244
252
  # Check the value of D.S.N. in anotherset
253
+ # The D.S.N. is neither an empty nor *.0.0
245
254
  as = Sisimai::SMTP::Status.find(anotherset['diagnosis'])
246
- if as.size > 0 && as[-4, 4] != '.0.0'
247
- # The D.S.N. is neither an empty nor *.0.0
248
- e['status'] = as
249
- end
255
+ e['status'] = as if Sisimai::SMTP::Status.is_ambiguous(as) == false
250
256
  end
251
257
 
252
258
  if e['replycode'].empty? || e['replycode'].end_with?('00')
@@ -72,9 +72,9 @@ module Sisimai::Lhost
72
72
  OnHoldPair = [" does not like recipient.", "this message has been in the queue too long."].freeze
73
73
  FailOnLDAP = {
74
74
  # qmail-ldap-1.03-20040101.patch:19817 - 19866
75
- "exceedlimit" => ["The message exeeded the maximum size the user accepts"], # 5.2.3
76
- "userunknown" => ["Sorry, no mailbox here by that name"], # 5.1.1
77
- "suspend" => [ # 5.2.1
75
+ "emailtoolarge" => ["The message exeeded the maximum size the user accepts"], # 5.2.3
76
+ "userunknown" => ["Sorry, no mailbox here by that name"], # 5.1.1
77
+ "suspend" => [ # 5.2.1
78
78
  "Mailaddress is administrativly disabled",
79
79
  "Mailaddress is administrativley disabled",
80
80
  "Mailaddress is administratively disabled",
@@ -102,8 +102,8 @@ module Sisimai::Lhost
102
102
  "mailboxfull" => ["disk quota exceeded"],
103
103
  # qmail-qmtpd.c:233| ... result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
104
104
  # qmail-smtpd.c:391| ... out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return;
105
- "mesgtoobig" => ["Message size exceeds fixed maximum message size:"],
106
- "networkerror"=> [
105
+ "emailtoolarge" => ["Message size exceeds fixed maximum message size:"],
106
+ "networkerror" => [
107
107
  "Sorry, I wasn't able to establish an SMTP connection",
108
108
  "Sorry. Although I'm listed as a best-preference MX or A for that host",
109
109
  ],
@@ -182,7 +182,7 @@ module Sisimai::Lhost
182
182
  next if v["rhost"] != ""
183
183
  StartingOf["rhost"].each do |r|
184
184
  # Find a remote host name
185
- p1 = e.index(r); next unless p1
185
+ p1 = e.index(r); next if p1.nil?
186
186
  cm = r.size
187
187
  p2 = e.index(" ", p1 + cm + 1) || p2 = e.rindex(".")
188
188
 
@@ -191,7 +191,7 @@ module Sisimai::Lhost
191
191
  end
192
192
  end
193
193
  end
194
- return nil unless recipients > 0
194
+ return nil if recipients == 0
195
195
 
196
196
  dscontents.each do |e|
197
197
  # Tidy up the error message in e['diagnosis'], Try to detect the bounce reason.
@@ -200,7 +200,7 @@ module Sisimai::Lhost
200
200
  # Get the SMTP command name for the session
201
201
  CommandSet.each_key do |r|
202
202
  # Get the last SMTP command
203
- next unless CommandSet[r].any? { |a| e["diagnosis"].include?(a) }
203
+ next if CommandSet[r].none? { |a| e["diagnosis"].include?(a) }
204
204
  e["command"] = r
205
205
  break
206
206
  end
@@ -224,10 +224,10 @@ module Sisimai::Lhost
224
224
  [e["alterrors"], e["diagnosis"]].each do |f|
225
225
  # Try to detect an error reason
226
226
  break if e["reason"] != ""
227
- next unless f
227
+ next if f.nil?
228
228
  MessagesOf.each_key do |r|
229
229
  # The key is a bounce reason name
230
- next unless MessagesOf[r].any? { |a| f.include?(a) }
230
+ next if MessagesOf[r].none? { |a| f.include?(a) }
231
231
  e["reason"] = r
232
232
  break
233
233
  end
@@ -235,7 +235,7 @@ module Sisimai::Lhost
235
235
 
236
236
  FailOnLDAP.each_key do |r|
237
237
  # The key is a bounce reason name
238
- next unless FailOnLDAP[r].any? { |a| f.include?(a) }
238
+ next if FailOnLDAP[r].none? { |a| f.include?(a) }
239
239
  e["reason"] = r
240
240
  break
241
241
  end
@@ -31,10 +31,10 @@ module Sisimai::Lhost
31
31
  # @return [Nil] it failed to decode or the arguments are missing
32
32
  def inquire(mhead, mbody)
33
33
  return nil if mhead['x-aol-ip']
34
- match = nil
34
+ match = false
35
35
  match ||= true if mhead['subject'].end_with?('see transcript for details')
36
36
  match ||= true if mhead['subject'].start_with?('Warning: ')
37
- return nil unless match
37
+ return nil if match == false
38
38
 
39
39
  fieldtable = Sisimai::RFC1894.FIELDTABLE
40
40
  permessage = {} # (Hash) Store values of each Per-Message field
@@ -89,11 +89,11 @@ module Sisimai::Lhost
89
89
  v['diagnosis'] = o[2]
90
90
  else
91
91
  # Other DSN fields defined in RFC3464
92
- next unless fieldtable[o[0]]
92
+ next if fieldtable[o[0]].nil?
93
93
  next if o[3] == "host" && Sisimai::RFC1123.is_internethost(o[2]) == false
94
94
  v[fieldtable[o[0]]] = o[2]
95
95
 
96
- next unless f == 1
96
+ next if f != 1
97
97
  permessage[fieldtable[o[0]]] = o[2]
98
98
  end
99
99
  else
@@ -108,7 +108,7 @@ module Sisimai::Lhost
108
108
  # Reporting-MTA: dns; mx.example.jp
109
109
  # Received-From-MTA: DNS; x1x2x3x4.dhcp.example.ne.jp
110
110
  # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
111
- unless e.start_with?(' ')
111
+ if e.start_with?(' ') == false
112
112
  if e.start_with?('>>> ')
113
113
  # >>> DATA (Client Command)
114
114
  thecommand = Sisimai::SMTP::Command.find(e) if thecommand.empty?
@@ -116,7 +116,7 @@ module Sisimai::Lhost
116
116
  elsif e.start_with?('<<< ')
117
117
  # <<< Response from the SMTP server
118
118
  cv = e[4, e.size - 4]
119
- esmtpreply << cv unless esmtpreply.index(cv)
119
+ esmtpreply << cv if esmtpreply.index(cv).nil?
120
120
  else
121
121
  # Detect an SMTP session error or a connection error
122
122
  next if sessionerr
@@ -157,14 +157,14 @@ module Sisimai::Lhost
157
157
  end
158
158
  else
159
159
  # Continued line of the value of Diagnostic-Code field
160
- next unless readslices[-2].start_with?('Diagnostic-Code:')
161
- next unless e.start_with?(' ')
160
+ next if readslices[-2].start_with?('Diagnostic-Code:') == false
161
+ next if e.start_with?(' ') == false
162
162
  v['diagnosis'] += " #{Sisimai::String.sweep(e)}"
163
163
  readslices[-1] = "Diagnostic-Code: #{e}"
164
164
  end
165
165
  end
166
166
  end # End of message/delivery-status
167
- return nil unless recipients > 0
167
+ return nil if recipients == 0
168
168
 
169
169
  dscontents.each do |e|
170
170
  # Set default values if each value is empty.
@@ -193,9 +193,9 @@ module Sisimai::Lhost
193
193
 
194
194
  while true
195
195
  # Check alternative status code and override it
196
- break unless anotherset.has_key?('status')
197
- break unless anotherset['status'].size > 0
198
- break if Sisimai::SMTP::Status.test(e['status'])
196
+ break if anotherset.has_key?('status') == false
197
+ break if anotherset['status'].size == 0
198
+ break if Sisimai::SMTP::Status.test(e['status'])
199
199
 
200
200
  e['status'] = anotherset['status']
201
201
  break
@@ -203,7 +203,7 @@ module Sisimai::Lhost
203
203
 
204
204
  # @example.jp, no local part
205
205
  # # Get email address from the value of Diagnostic-Code field
206
- next unless e['recipient'].start_with?('@')
206
+ next if e['recipient'].start_with?('@') == false
207
207
  cv = Sisimai::Address.find(e['diagnosis'], true) || []
208
208
  e['recipient'] = cv[0][:address] if cv.size > 0
209
209
  end
@@ -1,13 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::InterScanMSS decodes a bounce email which created by Trend Micro InterScan
3
- # Messaging Security Suite https://www.trendmicro.com/en_us/business/products/user-protection/sps/email-and-collaboration/interscan-messaging.html.
2
+ # Sisimai::Lhost::TrendMicro decodes a bounce email which created by TREND VISION ONE: Email and
3
+ # Collaboration Security: https://www.trendmicro.com/en_us/business/products/email-and-collaboration.html
4
4
  # Methods in the module are called from only Sisimai::Message.
5
- module InterScanMSS
5
+ module TrendMicro
6
6
  class << self
7
7
  require 'sisimai/lhost'
8
8
  Boundaries = ['Content-Type: message/rfc822'].freeze
9
9
 
10
- # @abstract Decodes the bounce message from Trend Micro InterScanMSS Messaging Secutiry Suie
10
+ # @abstract Decodes the bounce message from Trend Micro TREND VISION ONE
11
11
  # @param [Hash] mhead Message headers of a bounce email
12
12
  # @param [String] mbody Message body of a bounce email
13
13
  # @return [Hash] Bounce data list and message/rfc822 part
@@ -23,7 +23,7 @@ module Sisimai::Lhost
23
23
  match += 1 if mhead['from'].start_with?('"InterScan MSS"')
24
24
  match += 1 if mhead['from'].start_with?('"InterScan Notification"')
25
25
  match += 1 if tryto.any? { |a| mhead['subject'] == a }
26
- return nil unless match > 0
26
+ return nil if match == 0
27
27
 
28
28
  require 'sisimai/smtp/command'
29
29
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
@@ -65,12 +65,11 @@ module Sisimai::Lhost
65
65
  else
66
66
  # Error message in non-English
67
67
  v['command'] = Sisimai::SMTP::Command.find(e) if e.include?(' >>> ')
68
- p3 = e.index(' <<< ')
69
- next unless p3
68
+ p3 = e.index(' <<< '); next if p3.nil?
70
69
  v['diagnosis'] = e[p3 + 4, e.size]
71
70
  end
72
71
  end
73
- return nil unless recipients > 0
72
+ return nil if recipients == 0
74
73
 
75
74
  dscontents.each do |e|
76
75
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
@@ -78,7 +77,7 @@ module Sisimai::Lhost
78
77
  end
79
78
  return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
80
79
  end
81
- def description; return 'Trend Micro InterScan Messaging Security Suite'; end
80
+ def description; return 'TREND VISION ONE(Email and Collaboration Security)'; end
82
81
  end
83
82
  end
84
83
  end